├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── nano-lib-rs ├── Cargo.toml └── src │ ├── block.rs │ ├── error.rs │ ├── hash.rs │ ├── keys.rs │ ├── lib.rs │ ├── macros.rs │ └── message.rs ├── nanopow-rs ├── Cargo.toml ├── README.md ├── Web.toml ├── benches │ └── bench.rs └── src │ ├── error.rs │ └── lib.rs └── src ├── error.rs ├── main.rs ├── net ├── codec.rs ├── mod.rs └── udp_framed.rs ├── node ├── handler.rs ├── mod.rs └── state.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | log/ 3 | /target/ 4 | **/*.rs.bk 5 | .vscode -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aho-corasick" 3 | version = "0.6.4" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | dependencies = [ 6 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "ansi_term" 11 | version = "0.11.0" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | dependencies = [ 14 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 15 | ] 16 | 17 | [[package]] 18 | name = "arrayref" 19 | version = "0.3.4" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | 22 | [[package]] 23 | name = "arrayvec" 24 | version = "0.4.7" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | dependencies = [ 27 | "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 28 | ] 29 | 30 | [[package]] 31 | name = "atty" 32 | version = "0.2.8" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | dependencies = [ 35 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "backtrace" 42 | version = "0.3.5" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 46 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 47 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 48 | "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 50 | ] 51 | 52 | [[package]] 53 | name = "backtrace-sys" 54 | version = "0.1.16" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | dependencies = [ 57 | "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 58 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "bincode" 63 | version = "1.0.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | dependencies = [ 66 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 67 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 68 | ] 69 | 70 | [[package]] 71 | name = "bitflags" 72 | version = "1.0.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | 75 | [[package]] 76 | name = "blake2" 77 | version = "0.7.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | dependencies = [ 80 | "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "crypto-mac 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 83 | ] 84 | 85 | [[package]] 86 | name = "byte-tools" 87 | version = "0.2.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | 90 | [[package]] 91 | name = "byteorder" 92 | version = "1.2.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | 95 | [[package]] 96 | name = "bytes" 97 | version = "0.4.6" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | dependencies = [ 100 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 103 | ] 104 | 105 | [[package]] 106 | name = "cast" 107 | version = "0.2.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | 110 | [[package]] 111 | name = "cc" 112 | version = "1.0.9" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | 115 | [[package]] 116 | name = "cfg-if" 117 | version = "0.1.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | 120 | [[package]] 121 | name = "chrono" 122 | version = "0.4.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | dependencies = [ 125 | "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 126 | "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 127 | ] 128 | 129 | [[package]] 130 | name = "clap" 131 | version = "2.31.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | dependencies = [ 134 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 137 | "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 140 | "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 141 | ] 142 | 143 | [[package]] 144 | name = "clear_on_drop" 145 | version = "0.2.3" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | dependencies = [ 148 | "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 149 | ] 150 | 151 | [[package]] 152 | name = "constant_time_eq" 153 | version = "0.1.3" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | 156 | [[package]] 157 | name = "criterion" 158 | version = "0.2.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "criterion-plot 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 164 | "criterion-stats 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 167 | "handlebars 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", 169 | "itertools-num 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 170 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "simplelog 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 175 | ] 176 | 177 | [[package]] 178 | name = "criterion-plot" 179 | version = "0.2.1" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | dependencies = [ 182 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 183 | "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 184 | "itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", 185 | ] 186 | 187 | [[package]] 188 | name = "criterion-stats" 189 | version = "0.2.1" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | dependencies = [ 192 | "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 196 | "thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 197 | ] 198 | 199 | [[package]] 200 | name = "crossbeam-channel" 201 | version = "0.1.2" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | dependencies = [ 204 | "crossbeam-epoch 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 207 | ] 208 | 209 | [[package]] 210 | name = "crossbeam-deque" 211 | version = "0.3.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 216 | ] 217 | 218 | [[package]] 219 | name = "crossbeam-epoch" 220 | version = "0.2.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | dependencies = [ 223 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 224 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 225 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 226 | "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 227 | "memoffset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 228 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 229 | ] 230 | 231 | [[package]] 232 | name = "crossbeam-epoch" 233 | version = "0.4.1" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | dependencies = [ 236 | "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 237 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 238 | "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 239 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 240 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 242 | ] 243 | 244 | [[package]] 245 | name = "crossbeam-utils" 246 | version = "0.2.2" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | dependencies = [ 249 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 250 | ] 251 | 252 | [[package]] 253 | name = "crossbeam-utils" 254 | version = "0.3.2" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | dependencies = [ 257 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 258 | ] 259 | 260 | [[package]] 261 | name = "crypto-mac" 262 | version = "0.5.2" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | dependencies = [ 265 | "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 267 | ] 268 | 269 | [[package]] 270 | name = "curve25519-dalek" 271 | version = "0.14.4" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | dependencies = [ 274 | "arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 278 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 279 | "subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 280 | ] 281 | 282 | [[package]] 283 | name = "data-encoding" 284 | version = "2.1.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | 287 | [[package]] 288 | name = "data-encoding-macro" 289 | version = "0.1.1" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "data-encoding-macro-internal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 295 | ] 296 | 297 | [[package]] 298 | name = "data-encoding-macro-internal" 299 | version = "0.1.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | dependencies = [ 302 | "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 305 | ] 306 | 307 | [[package]] 308 | name = "difference" 309 | version = "2.0.0" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | 312 | [[package]] 313 | name = "digest" 314 | version = "0.7.2" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 318 | ] 319 | 320 | [[package]] 321 | name = "dtoa" 322 | version = "0.4.2" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | 325 | [[package]] 326 | name = "ed25519-dalek" 327 | version = "0.6.1" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | dependencies = [ 330 | "curve25519-dalek 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)", 331 | "digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 332 | "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 336 | ] 337 | 338 | [[package]] 339 | name = "either" 340 | version = "1.4.0" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | 343 | [[package]] 344 | name = "error-chain" 345 | version = "0.11.0" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | dependencies = [ 348 | "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 349 | ] 350 | 351 | [[package]] 352 | name = "failure" 353 | version = "0.1.1" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | dependencies = [ 356 | "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 358 | ] 359 | 360 | [[package]] 361 | name = "failure_derive" 362 | version = "0.1.1" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | dependencies = [ 365 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 368 | ] 369 | 370 | [[package]] 371 | name = "fern" 372 | version = "0.5.4" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | dependencies = [ 375 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 376 | ] 377 | 378 | [[package]] 379 | name = "fuchsia-zircon" 380 | version = "0.3.3" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | dependencies = [ 383 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 385 | ] 386 | 387 | [[package]] 388 | name = "fuchsia-zircon-sys" 389 | version = "0.3.3" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | 392 | [[package]] 393 | name = "futures" 394 | version = "0.1.21" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | 397 | [[package]] 398 | name = "generic-array" 399 | version = "0.9.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | dependencies = [ 402 | "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 403 | ] 404 | 405 | [[package]] 406 | name = "handlebars" 407 | version = "0.31.0" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | dependencies = [ 410 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 411 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 412 | "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 413 | "pest_derive 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 414 | "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 415 | "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", 416 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 417 | "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 418 | ] 419 | 420 | [[package]] 421 | name = "indexmap" 422 | version = "1.0.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | 425 | [[package]] 426 | name = "iovec" 427 | version = "0.1.2" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | dependencies = [ 430 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 431 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 432 | ] 433 | 434 | [[package]] 435 | name = "itertools" 436 | version = "0.7.7" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | dependencies = [ 439 | "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 440 | ] 441 | 442 | [[package]] 443 | name = "itertools-num" 444 | version = "0.1.1" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | dependencies = [ 447 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 448 | ] 449 | 450 | [[package]] 451 | name = "itoa" 452 | version = "0.4.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | 455 | [[package]] 456 | name = "kernel32-sys" 457 | version = "0.2.2" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | dependencies = [ 460 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 461 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 462 | ] 463 | 464 | [[package]] 465 | name = "lazy_static" 466 | version = "0.2.11" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | 469 | [[package]] 470 | name = "lazy_static" 471 | version = "1.0.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | 474 | [[package]] 475 | name = "lazycell" 476 | version = "0.6.0" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | 479 | [[package]] 480 | name = "libc" 481 | version = "0.2.39" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | 484 | [[package]] 485 | name = "log" 486 | version = "0.3.9" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | dependencies = [ 489 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 490 | ] 491 | 492 | [[package]] 493 | name = "log" 494 | version = "0.4.1" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | dependencies = [ 497 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 498 | ] 499 | 500 | [[package]] 501 | name = "memchr" 502 | version = "2.0.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 506 | ] 507 | 508 | [[package]] 509 | name = "memoffset" 510 | version = "0.1.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | 513 | [[package]] 514 | name = "memoffset" 515 | version = "0.2.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | 518 | [[package]] 519 | name = "mio" 520 | version = "0.6.14" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | dependencies = [ 523 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 524 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 525 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 526 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 527 | "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 528 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 529 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 530 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 531 | "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", 532 | "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 533 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 534 | ] 535 | 536 | [[package]] 537 | name = "miow" 538 | version = "0.2.1" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | dependencies = [ 541 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 542 | "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", 543 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 544 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 545 | ] 546 | 547 | [[package]] 548 | name = "nano-lib-rs" 549 | version = "0.0.1" 550 | dependencies = [ 551 | "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 552 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 553 | "blake2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 554 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 555 | "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 556 | "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 557 | "data-encoding-macro 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 558 | "ed25519-dalek 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", 559 | "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 560 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 561 | "nanopow-rs 0.4.0", 562 | "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 563 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 564 | "serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", 566 | "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 567 | ] 568 | 569 | [[package]] 570 | name = "nano-rs" 571 | version = "0.0.1" 572 | dependencies = [ 573 | "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 574 | "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 575 | "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", 576 | "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "fern 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "indexmap 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 581 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 582 | "nano-lib-rs 0.0.1", 583 | "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", 584 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 585 | "tokio 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 586 | "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 587 | "tokio-timer 0.1.2 (git+https://github.com/termhn/tokio-timer)", 588 | ] 589 | 590 | [[package]] 591 | name = "nanopow-rs" 592 | version = "0.4.0" 593 | dependencies = [ 594 | "blake2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 595 | "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 596 | "criterion 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 597 | "crossbeam-channel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 598 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 599 | "data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 601 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 602 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 603 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 604 | ] 605 | 606 | [[package]] 607 | name = "net2" 608 | version = "0.2.32" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | dependencies = [ 611 | "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 612 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 613 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 614 | ] 615 | 616 | [[package]] 617 | name = "nodrop" 618 | version = "0.1.12" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | 621 | [[package]] 622 | name = "num" 623 | version = "0.1.42" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | dependencies = [ 626 | "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 627 | "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", 628 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 629 | ] 630 | 631 | [[package]] 632 | name = "num-integer" 633 | version = "0.1.36" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | dependencies = [ 636 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 637 | ] 638 | 639 | [[package]] 640 | name = "num-iter" 641 | version = "0.1.35" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | dependencies = [ 644 | "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", 645 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 646 | ] 647 | 648 | [[package]] 649 | name = "num-traits" 650 | version = "0.1.43" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | dependencies = [ 653 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 654 | ] 655 | 656 | [[package]] 657 | name = "num-traits" 658 | version = "0.2.2" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | 661 | [[package]] 662 | name = "num_cpus" 663 | version = "1.8.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | dependencies = [ 666 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 667 | ] 668 | 669 | [[package]] 670 | name = "owning_ref" 671 | version = "0.3.3" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | dependencies = [ 674 | "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 675 | ] 676 | 677 | [[package]] 678 | name = "parking_lot" 679 | version = "0.4.8" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | dependencies = [ 682 | "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 683 | "parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 684 | ] 685 | 686 | [[package]] 687 | name = "parking_lot_core" 688 | version = "0.2.13" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | dependencies = [ 691 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 692 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 693 | "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 694 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 695 | ] 696 | 697 | [[package]] 698 | name = "pest" 699 | version = "1.0.6" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | 702 | [[package]] 703 | name = "pest_derive" 704 | version = "1.0.6" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | dependencies = [ 707 | "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 708 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 709 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 710 | ] 711 | 712 | [[package]] 713 | name = "pretty_assertions" 714 | version = "0.5.1" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | dependencies = [ 717 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 718 | "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 719 | ] 720 | 721 | [[package]] 722 | name = "proc-macro-hack" 723 | version = "0.4.0" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | dependencies = [ 726 | "proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 727 | ] 728 | 729 | [[package]] 730 | name = "proc-macro-hack-impl" 731 | version = "0.4.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | 734 | [[package]] 735 | name = "proc-macro2" 736 | version = "0.2.3" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | dependencies = [ 739 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 740 | ] 741 | 742 | [[package]] 743 | name = "quick-error" 744 | version = "1.2.1" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | 747 | [[package]] 748 | name = "quote" 749 | version = "0.3.15" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | 752 | [[package]] 753 | name = "quote" 754 | version = "0.4.2" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | dependencies = [ 757 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 758 | ] 759 | 760 | [[package]] 761 | name = "rand" 762 | version = "0.3.22" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | dependencies = [ 765 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 766 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 767 | "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 768 | ] 769 | 770 | [[package]] 771 | name = "rand" 772 | version = "0.4.2" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | dependencies = [ 775 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 776 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 777 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 778 | ] 779 | 780 | [[package]] 781 | name = "redox_syscall" 782 | version = "0.1.37" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | 785 | [[package]] 786 | name = "redox_termios" 787 | version = "0.1.1" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | dependencies = [ 790 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 791 | ] 792 | 793 | [[package]] 794 | name = "regex" 795 | version = "0.2.10" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | dependencies = [ 798 | "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", 799 | "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 800 | "regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 801 | "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 802 | "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 803 | ] 804 | 805 | [[package]] 806 | name = "regex-syntax" 807 | version = "0.5.3" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | dependencies = [ 810 | "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 811 | ] 812 | 813 | [[package]] 814 | name = "rustc-demangle" 815 | version = "0.1.7" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | 818 | [[package]] 819 | name = "scopeguard" 820 | version = "0.3.3" 821 | source = "registry+https://github.com/rust-lang/crates.io-index" 822 | 823 | [[package]] 824 | name = "serde" 825 | version = "1.0.33" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | 828 | [[package]] 829 | name = "serde_derive" 830 | version = "1.0.33" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | dependencies = [ 833 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 834 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 835 | "serde_derive_internals 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", 836 | "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", 837 | ] 838 | 839 | [[package]] 840 | name = "serde_derive_internals" 841 | version = "0.21.0" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | dependencies = [ 844 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 845 | "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", 846 | ] 847 | 848 | [[package]] 849 | name = "serde_json" 850 | version = "1.0.13" 851 | source = "registry+https://github.com/rust-lang/crates.io-index" 852 | dependencies = [ 853 | "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 854 | "itoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 855 | "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 856 | "serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", 857 | ] 858 | 859 | [[package]] 860 | name = "simplelog" 861 | version = "0.5.0" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | dependencies = [ 864 | "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 865 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 866 | "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 867 | ] 868 | 869 | [[package]] 870 | name = "slab" 871 | version = "0.3.0" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | 874 | [[package]] 875 | name = "slab" 876 | version = "0.4.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | 879 | [[package]] 880 | name = "smallvec" 881 | version = "0.6.0" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | 884 | [[package]] 885 | name = "stable_deref_trait" 886 | version = "1.0.0" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | 889 | [[package]] 890 | name = "strsim" 891 | version = "0.7.0" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | 894 | [[package]] 895 | name = "subtle" 896 | version = "0.3.0" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | dependencies = [ 899 | "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 900 | ] 901 | 902 | [[package]] 903 | name = "subtle" 904 | version = "0.5.1" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | 907 | [[package]] 908 | name = "syn" 909 | version = "0.11.11" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | dependencies = [ 912 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 913 | "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", 914 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 915 | ] 916 | 917 | [[package]] 918 | name = "syn" 919 | version = "0.12.14" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | dependencies = [ 922 | "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 923 | "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 924 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 925 | ] 926 | 927 | [[package]] 928 | name = "synom" 929 | version = "0.11.3" 930 | source = "registry+https://github.com/rust-lang/crates.io-index" 931 | dependencies = [ 932 | "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 933 | ] 934 | 935 | [[package]] 936 | name = "synstructure" 937 | version = "0.6.1" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | dependencies = [ 940 | "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", 941 | "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", 942 | ] 943 | 944 | [[package]] 945 | name = "term" 946 | version = "0.4.6" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | dependencies = [ 949 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 950 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 951 | ] 952 | 953 | [[package]] 954 | name = "termion" 955 | version = "1.5.1" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | dependencies = [ 958 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 959 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 960 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 961 | ] 962 | 963 | [[package]] 964 | name = "textwrap" 965 | version = "0.9.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | dependencies = [ 968 | "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 969 | ] 970 | 971 | [[package]] 972 | name = "thread-scoped" 973 | version = "1.0.2" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | 976 | [[package]] 977 | name = "thread_local" 978 | version = "0.3.5" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | dependencies = [ 981 | "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 982 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 983 | ] 984 | 985 | [[package]] 986 | name = "time" 987 | version = "0.1.39" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | dependencies = [ 990 | "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", 991 | "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", 992 | "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 993 | ] 994 | 995 | [[package]] 996 | name = "tokio" 997 | version = "0.1.3" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | dependencies = [ 1000 | "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1001 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1002 | "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 1003 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1004 | "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", 1005 | "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1006 | "tokio-executor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1007 | "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 1008 | "tokio-reactor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1009 | "tokio-threadpool 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "tokio-executor" 1014 | version = "0.1.0" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | dependencies = [ 1017 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "tokio-io" 1022 | version = "0.1.6" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | dependencies = [ 1025 | "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 1026 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1027 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1028 | ] 1029 | 1030 | [[package]] 1031 | name = "tokio-reactor" 1032 | version = "0.1.0" 1033 | source = "registry+https://github.com/rust-lang/crates.io-index" 1034 | dependencies = [ 1035 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1036 | "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 1037 | "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", 1038 | "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1039 | "tokio-executor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1040 | "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "tokio-threadpool" 1045 | version = "0.1.0" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | dependencies = [ 1048 | "crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1049 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1050 | "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 1051 | "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1052 | "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", 1053 | "tokio-executor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1054 | ] 1055 | 1056 | [[package]] 1057 | name = "tokio-timer" 1058 | version = "0.1.2" 1059 | source = "git+https://github.com/termhn/tokio-timer#e8a7ae8afafd103cb53386f5775fd5f6e2005936" 1060 | dependencies = [ 1061 | "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", 1062 | "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "typenum" 1067 | version = "1.10.0" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | 1070 | [[package]] 1071 | name = "ucd-util" 1072 | version = "0.1.1" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | 1075 | [[package]] 1076 | name = "unicode-width" 1077 | version = "0.1.4" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | 1080 | [[package]] 1081 | name = "unicode-xid" 1082 | version = "0.0.4" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | 1085 | [[package]] 1086 | name = "unicode-xid" 1087 | version = "0.1.0" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | 1090 | [[package]] 1091 | name = "unreachable" 1092 | version = "1.0.0" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | dependencies = [ 1095 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "utf8-ranges" 1100 | version = "1.0.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | 1103 | [[package]] 1104 | name = "vec_map" 1105 | version = "0.8.0" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | 1108 | [[package]] 1109 | name = "void" 1110 | version = "1.0.2" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | 1113 | [[package]] 1114 | name = "winapi" 1115 | version = "0.2.8" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | 1118 | [[package]] 1119 | name = "winapi" 1120 | version = "0.3.4" 1121 | source = "registry+https://github.com/rust-lang/crates.io-index" 1122 | dependencies = [ 1123 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1124 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "winapi-build" 1129 | version = "0.1.1" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | 1132 | [[package]] 1133 | name = "winapi-i686-pc-windows-gnu" 1134 | version = "0.4.0" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | 1137 | [[package]] 1138 | name = "winapi-x86_64-pc-windows-gnu" 1139 | version = "0.4.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | 1142 | [[package]] 1143 | name = "ws2_32-sys" 1144 | version = "0.2.1" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | dependencies = [ 1147 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 1148 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 1149 | ] 1150 | 1151 | [metadata] 1152 | "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" 1153 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 1154 | "checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f" 1155 | "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" 1156 | "checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" 1157 | "checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" 1158 | "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" 1159 | "checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0" 1160 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 1161 | "checksum blake2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0da79fe9391e02df251e0d86197332d398b4721e80722fc91b9c4cbde3ce355" 1162 | "checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" 1163 | "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" 1164 | "checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" 1165 | "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" 1166 | "checksum cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2b4911e4bdcb4100c7680e7e854ff38e23f1b34d4d9e079efae3da2801341ffc" 1167 | "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" 1168 | "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" 1169 | "checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" 1170 | "checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" 1171 | "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" 1172 | "checksum criterion 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e8852a0db5351470bbae05adaf2f8247eb50bf61abad84dcddad025157a916c" 1173 | "checksum criterion-plot 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f69a0a6c7232793bcb84a5c3a97bc4d4476905ec50d836f589e1e67faeb497" 1174 | "checksum criterion-stats 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40ecf8acabd9b6b0101845b07c55ce3f5e375306c34e51a20fe7e94485956bcf" 1175 | "checksum crossbeam-channel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7b07a3084d8718d95338443d5a46aab38ce16d5f991d4027a0906b369f70a3" 1176 | "checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2" 1177 | "checksum crossbeam-epoch 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9898f21d6d647793e163c804944941fb19aecd1f4a1a4c254bbb0bee15ccdea5" 1178 | "checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" 1179 | "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" 1180 | "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" 1181 | "checksum crypto-mac 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0999b4ff4d3446d4ddb19a63e9e00c1876e75cd7000d20e57a693b4b3f08d958" 1182 | "checksum curve25519-dalek 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6734ff1a930d90b3ee54b7d6eba1b520f8724a1f353cf4f2b4b171a9ce63d814" 1183 | "checksum data-encoding 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67df0571a74bf0d97fb8b2ed22abdd9a48475c96bd327db968b7d9cace99655e" 1184 | "checksum data-encoding-macro 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05d74fe5b9e438951801efbc8c2acca091f70008eebd6edef061f89105aa8cd7" 1185 | "checksum data-encoding-macro-internal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9f31cd0086d3b61c74c6d5a678e47aa3ac8b0c648dfc9165f8f97ccf1925cf1" 1186 | "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" 1187 | "checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603" 1188 | "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" 1189 | "checksum ed25519-dalek 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3692ef38cc617236a39120ef0b91794e5e4d5c96227607a6740bfaaab53ac3c" 1190 | "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" 1191 | "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" 1192 | "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" 1193 | "checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" 1194 | "checksum fern 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "50475651fccc56343c766e4d1889428ea753308a977e1315db358ada28cc8c9d" 1195 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1196 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1197 | "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" 1198 | "checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" 1199 | "checksum handlebars 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7bdb08e879b8c78ee90f5022d121897c31ea022cb0cc6d13f2158c7a9fbabb1" 1200 | "checksum indexmap 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9378f1f3923647a9aea6af4c6b5de68cc8a71415459ad25ef191191c48f5b7" 1201 | "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" 1202 | "checksum itertools 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "23d53b4c7394338044c3b9c8c5b2caaf7b40ae049ecd321578ebdc2e13738cd1" 1203 | "checksum itertools-num 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d78fa608383e6e608ba36f962ac991d5d6878d7203eb93b4711b14fa6717813" 1204 | "checksum itoa 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92a9df60778f789c37f76778ae8d0a2471c41baa8b059d98a5873c978f549587" 1205 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1206 | "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 1207 | "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" 1208 | "checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" 1209 | "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" 1210 | "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 1211 | "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" 1212 | "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" 1213 | "checksum memoffset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e163e5baece1a039e71e75b074de17a9b4114982aa109921fc20253bdf91a53c" 1214 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 1215 | "checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" 1216 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1217 | "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" 1218 | "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" 1219 | "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" 1220 | "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" 1221 | "checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" 1222 | "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" 1223 | "checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" 1224 | "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" 1225 | "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" 1226 | "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" 1227 | "checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79" 1228 | "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" 1229 | "checksum pest_derive 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6453faedc5c9980a3c278f28b1df33344a79cc6d4a2fd96e2b56288374dc822a" 1230 | "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" 1231 | "checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0" 1232 | "checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892" 1233 | "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" 1234 | "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" 1235 | "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 1236 | "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" 1237 | "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" 1238 | "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" 1239 | "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" 1240 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 1241 | "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" 1242 | "checksum regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2550876c31dc914696a6c2e01cbce8afba79a93c8ae979d2fe051c0230b3756" 1243 | "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" 1244 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 1245 | "checksum serde 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe95aa0d46f04ce5c3a88bdcd4114ecd6144ed0b2725ebca2f1127744357807" 1246 | "checksum serde_derive 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "23b163a6ce7e1aa897919f9d8e40bd1f8a6f95342ed57727ae31387a01a7a356" 1247 | "checksum serde_derive_internals 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "370aa477297975243dc914d0b0e1234927520ec311de507a560fbd1c80f7ab8c" 1248 | "checksum serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5c508584d9913df116b91505eec55610a2f5b16e9ed793c46e4d0152872b3e74" 1249 | "checksum simplelog 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0982c03fa49b24fdf9422d776c2d38b6cfa4b2073ad3d0848bcabcf5a704b9" 1250 | "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" 1251 | "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" 1252 | "checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" 1253 | "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" 1254 | "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" 1255 | "checksum subtle 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7a6bab57c3efd01ebd3d750f4244ae0af4cdd1fc505a7904a41603192b803c5" 1256 | "checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb" 1257 | "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 1258 | "checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" 1259 | "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 1260 | "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" 1261 | "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 1262 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 1263 | "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" 1264 | "checksum thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcbb6aa301e5d3b0b5ef639c9a9c7e2f1c944f177b460c04dc24c69b1fa2bd99" 1265 | "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" 1266 | "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" 1267 | "checksum tokio 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "490c5ff233997a62649c0a7b523b25a1cc6fab1389b3faed0d72e8bdcef7b0ad" 1268 | "checksum tokio-executor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46108c2aca0eb4b9a883bf37a28d122ca70f5318446f59f729cd1ff78a0bb5fb" 1269 | "checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a" 1270 | "checksum tokio-reactor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f21d00eb356854d502b81776cec931d12771e4ed6d198478d23ffd38c19279af" 1271 | "checksum tokio-threadpool 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "19a8656c45ae7893c9090ac5c98749e7ff904932973fabd541463f82628efacb" 1272 | "checksum tokio-timer 0.1.2 (git+https://github.com/termhn/tokio-timer)" = "" 1273 | "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" 1274 | "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" 1275 | "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" 1276 | "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 1277 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 1278 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 1279 | "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" 1280 | "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" 1281 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1282 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1283 | "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" 1284 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1285 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1286 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1287 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1288 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nano-rs" 3 | version = "0.0.1" 4 | authors = ["Gray Olson "] 5 | repository = "https://github.com/termhn/nano-rs" 6 | license = "Mozilla Public License 2.0" 7 | 8 | [workspace] 9 | members = [ 10 | "./", 11 | "nano-lib-rs", 12 | "nanopow-rs" 13 | ] 14 | 15 | [dependencies] 16 | tokio = "0.1" 17 | tokio-io = "0.1" 18 | tokio-timer = {git = "https://github.com/termhn/tokio-timer"} 19 | futures = "0.1" 20 | error-chain = "0.11" 21 | nano-lib-rs = {path = "./nano-lib-rs"} 22 | log = "0.4" 23 | fern = "0.5" 24 | chrono = "0.4" 25 | bytes = "0.4" 26 | data-encoding = "2.1" 27 | rand = "0.4" 28 | indexmap = "1.0" 29 | net2 = "0.2" 30 | clap = "~2.31.2" 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nano-rs 2 | 3 | Nano-rs is a pure Rust implementation of the Nano cryptocurrency based on Tokio. It is currently in its very infancy. 4 | 5 | This repo serves as a monorepo that contains several crates, including the root `nano-rs` crate, which is the node implementation itself. There are also several subcrates: 6 | 7 | - `nanopow-rs` is a standalone Proof of Work implementation. 8 | - `nano-lib-rs` provides types and functions for working with the Nano protocol in Rust. 9 | 10 | ## Milestones 11 | 12 | - [ ] A basic node that can validate and store blocks sent to it 13 | - [ ] Data structures 14 | - [x] Blocks 15 | - [x] Message headers 16 | - [ ] Message body 17 | - [x] Block 18 | - [X] Keepalive peers 19 | - [ ] Confirm Ack (votes etc) 20 | - [ ] Others... 21 | - [ ] Database 22 | - [x] Proof of work 23 | - [ ] Cryptographic functions 24 | - [ ] Basic wallet functions 25 | - [ ] Networking 26 | - [ ] Receiving keepalives and blocks 27 | - [x] keepalives 28 | - [x] publish 29 | - [x] confirm_req 30 | - [ ] confirm_ack 31 | - [ ] others... 32 | - [x] Sending keepalives 33 | - [ ] Add broadcasting and discovery 34 | - [ ] Add RPC interface 35 | - [ ] Add voting 36 | - [ ] Add compatibility with existing Nano Nodes 37 | - [ ] Add complete testing harness 38 | - [ ] Possibly more things in the future 39 | 40 | ## Installation/Usage 41 | 42 | First, [install Rust](https://rustup.rs/). 43 | 44 | Then make sure you're running nightly rust: 45 | 46 | ```sh 47 | rustup toolchain install nightly 48 | rustup default nightly 49 | ``` 50 | 51 | Then, 52 | 53 | ```sh 54 | git clone https://github.com/termhn/nano-rs 55 | cd nano-rs 56 | cargo run --release 57 | ``` 58 | 59 | Logging is printed to stderr and saved in files in the `logs/` folder. 60 | -------------------------------------------------------------------------------- /nano-lib-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nano-lib-rs" 3 | version = "0.0.1" 4 | authors = ["Gray Olson "] 5 | repository = "https://github.com/termhn/nano-rs" 6 | license = "Mozilla Public License 2.0" 7 | 8 | [dependencies] 9 | log = "0.4" 10 | bitflags = "1.0" 11 | byteorder = { version = "1.2", features = ["i128"] } 12 | error-chain = "0.11" 13 | bytes = { version = "0.4", features = ["serde"] } 14 | blake2 = "0.7" 15 | ed25519-dalek = { version = "0.6", features = ["nightly"] } 16 | nanopow-rs = { path = "../nanopow-rs" } 17 | data-encoding = "2.1" 18 | data-encoding-macro = "0.1.1" 19 | serde = "1.0" 20 | serde_derive = "1.0" 21 | serde_json = "1.0" 22 | bincode = "1.0" 23 | tokio-io = "0.1" 24 | 25 | [dev-dependencies] 26 | pretty_assertions = "0.5" -------------------------------------------------------------------------------- /nano-lib-rs/src/block.rs: -------------------------------------------------------------------------------- 1 | extern crate nanopow_rs; 2 | pub use nanopow_rs::{InputHash, Work}; 3 | 4 | use byteorder::{BigEndian, LittleEndian, ByteOrder}; 5 | 6 | use bytes::{Bytes, BytesMut, BufMut, Buf, IntoBuf}; 7 | use blake2::Blake2b; 8 | use blake2::digest::{Input, VariableOutput}; 9 | 10 | use hash::{Hash, Hasher}; 11 | use keys::{SecretKey, PublicKey, Signature, SIGNATURE_LENGTH}; 12 | use error::*; 13 | 14 | use data_encoding::HEXUPPER; 15 | 16 | use std::fmt; 17 | 18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 19 | pub struct BlockHash([u8; 32]); 20 | 21 | impl BlockHash { 22 | /// Convert hexadecimal formatted data into a BlockHash 23 | pub fn from_hex>(s: T) -> Result { 24 | let bytes = s.as_ref(); 25 | if bytes.len() != 64 { 26 | bail!(ErrorKind::BlockHashLengthError); 27 | } 28 | let mut buf = [0u8; 32]; 29 | let _ = HEXUPPER 30 | .decode_mut(bytes, &mut buf) 31 | .map_err::(|e| ErrorKind::InvalidHexCharacterError(e.error.position).into())?; 32 | Ok(BlockHash(buf)) 33 | } 34 | 35 | /// Create a BlockHash from a raw byte slice 36 | pub fn from_bytes>(bytes: T) -> Result { 37 | let bytes = bytes.as_ref(); 38 | if bytes.len() != 32 { 39 | bail!(ErrorKind::BlockHashLengthError); 40 | } 41 | let mut buf = [0u8; 32]; 42 | for i in 0..32 { 43 | buf[i] = bytes[i]; 44 | } 45 | Ok(BlockHash(buf)) 46 | } 47 | 48 | /// View the hash as a byte slice 49 | pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { 50 | &(self.0) 51 | } 52 | } 53 | 54 | impl Hash for BlockHash { 55 | fn hash(&self, state: &mut H) { 56 | state.write(&self.0[..]) 57 | } 58 | } 59 | 60 | impl From for String { 61 | fn from(hash: BlockHash) -> Self { 62 | let string = HEXUPPER.encode(&hash.0); 63 | string 64 | } 65 | } 66 | 67 | impl From for InputHash { 68 | fn from(hash: BlockHash) -> Self { 69 | InputHash::new(hash.0) 70 | } 71 | } 72 | 73 | enum_byte!(BlockKind { 74 | Invalid = 0x00, 75 | NotABlock = 0x01, 76 | Send = 0x02, 77 | Receive = 0x03, 78 | Open = 0x04, 79 | Change = 0x05, 80 | State = 0x06, 81 | }); 82 | 83 | impl BlockKind { 84 | pub fn size(&self) -> usize { 85 | match *self { 86 | BlockKind::Invalid => 0, 87 | BlockKind::NotABlock => 0, 88 | BlockKind::Send => 80, 89 | BlockKind::Receive => 64, 90 | BlockKind::Open => 96, 91 | BlockKind::Change => 32, 92 | BlockKind::State => 144, 93 | } 94 | } 95 | } 96 | 97 | #[derive(Clone, Debug, PartialEq, Eq)] 98 | pub struct Block { 99 | pub kind: BlockKind, 100 | pub payload: Option, 101 | pub next: Option, 102 | pub work: Option, 103 | pub signature: Option, 104 | pub hash: Option 105 | } 106 | 107 | impl Block { 108 | pub fn new(kind: BlockKind, payload: Option, signature: Option, work: Option) -> Self { 109 | Block { 110 | kind, 111 | payload, 112 | next: None, 113 | work, 114 | signature, 115 | hash: None, 116 | } 117 | } 118 | pub fn next(&self) -> Option { 119 | self.next 120 | } 121 | pub fn signature(&self) -> Option { 122 | self.signature 123 | } 124 | pub fn sign(&mut self, _key: &SecretKey) -> Result<()> { 125 | unimplemented!(); 126 | } 127 | pub fn work(&self) -> Option { 128 | self.work.clone() 129 | } 130 | pub fn set_work(&mut self, work: Work) -> Result<()> { 131 | if let Some(ref p) = self.payload { 132 | let valid = nanopow_rs::check_work(&p.work_source(), &work); 133 | if valid { 134 | bail!(ErrorKind::InvalidWorkError); 135 | } 136 | self.work = Some(work); 137 | Ok(()) 138 | } else { 139 | bail!("Cannot set work for a block with no payload"); 140 | } 141 | } 142 | pub fn generate_work(&mut self) -> Option { 143 | if let Some(ref p) = self.payload { 144 | let work = nanopow_rs::generate_work(&p.work_source(), None); 145 | self.work = work; 146 | work 147 | } else { 148 | None 149 | } 150 | } 151 | pub fn verify_work(&self) -> Result { 152 | if let Some(ref p) = self.payload { 153 | if let Some(ref w) = self.work { 154 | return Ok(nanopow_rs::check_work(&p.work_source(), w)) 155 | } 156 | } 157 | bail!(ErrorKind::NoWorkError); 158 | } 159 | pub fn cached_hash(&self) -> Option { 160 | self.hash 161 | } 162 | pub fn calculate_hash(&mut self) -> Result { 163 | if let Some(ref p) = self.payload { 164 | let mut hasher = BlockHasher::new(); 165 | p.hash(&mut hasher); 166 | let hash = hasher.finish()?; 167 | self.hash = Some(hash); 168 | Ok(hash) 169 | } else { 170 | bail!("Cannot calculate hash for block without payload") 171 | } 172 | } 173 | pub fn is_signed(&self) -> bool { 174 | self.signature().is_some() 175 | } 176 | pub fn has_work(&self) -> bool { 177 | self.work().is_some() 178 | } 179 | pub fn hash(&mut self, force: bool) -> Result { 180 | if !force { 181 | let cached_hash = self.cached_hash(); 182 | if let Some(hash) = cached_hash { 183 | return Ok(hash); 184 | } 185 | } 186 | self.calculate_hash() 187 | } 188 | pub fn serialize_bytes(&self) -> Bytes { 189 | if let Some(ref p) = self.payload { 190 | let mut buf = BytesMut::new(); 191 | p.serialize_bytes(&mut buf); 192 | if let Some(ref s) = self.signature { 193 | buf.reserve(SIGNATURE_LENGTH); 194 | buf.put_slice(&s.to_bytes()); 195 | } 196 | if let Some(ref w) = self.work { 197 | buf.reserve(8); 198 | if self.kind != BlockKind::State { 199 | buf.put_u64::(w.0); 200 | } else { 201 | buf.put_u64::(w.0); 202 | } 203 | } 204 | Bytes::from(buf) 205 | } else { 206 | Bytes::with_capacity(0) 207 | } 208 | } 209 | pub fn deserialize_bytes(bytes: Bytes, kind: BlockKind) -> Result { 210 | Ok(match kind { 211 | BlockKind::Invalid | BlockKind::NotABlock => { 212 | Block::new(kind, None, None, None) 213 | }, 214 | _ => { 215 | let len = bytes.len(); 216 | if len < kind.size() + SIGNATURE_LENGTH { 217 | bail!(ErrorKind::BlockParseError(BlockParseErrorKind::NoSignature)); 218 | } else if len < kind.size() + SIGNATURE_LENGTH + 8 { 219 | bail!(ErrorKind::BlockParseError(BlockParseErrorKind::NoWork)); 220 | } 221 | let mut buf = bytes.into_buf(); 222 | let payload = BlockPayload::deserialize_bytes(&mut buf, kind)?; 223 | let mut sig_buf = [0u8; 64]; 224 | buf.copy_to_slice(&mut sig_buf); 225 | let signature = Signature::from_bytes(&sig_buf)?; 226 | let work = if kind == BlockKind::State { 227 | Work(buf.get_u64::()) 228 | } else { 229 | Work(buf.get_u64::()) 230 | }; 231 | Block::new(kind, Some(payload), Some(signature), Some(work)) 232 | } 233 | }) 234 | } 235 | } 236 | 237 | pub struct BlockHasher { 238 | blake: Blake2b, 239 | } 240 | 241 | impl BlockHasher { 242 | pub fn new() -> Self { 243 | BlockHasher { 244 | blake: Blake2b::new(32).unwrap(), 245 | } 246 | } 247 | } 248 | 249 | impl Hasher for BlockHasher { 250 | type Output = BlockHash; 251 | fn write(&mut self, bytes: &[u8]) { 252 | self.blake.process(bytes); 253 | } 254 | 255 | fn finish(self) -> Result { 256 | let mut buf = [0u8; 32]; 257 | self.blake 258 | .variable_result(&mut buf) 259 | .map_err(|_| "Invalid key length")?; 260 | Ok(BlockHash(buf)) 261 | } 262 | } 263 | 264 | pub trait BufMutExt: BufMut { 265 | fn put_i128(&mut self, n: i128) { 266 | let mut buf = [0u8; 16]; 267 | T::write_i128(&mut buf, n); 268 | self.put_slice(&buf) 269 | } 270 | 271 | fn put_u128(&mut self, n: u128) { 272 | let mut buf = [0u8; 16]; 273 | T::write_u128(&mut buf, n); 274 | self.put_slice(&buf) 275 | } 276 | } 277 | 278 | impl BufMutExt for T {} 279 | 280 | pub trait BufExt: Buf { 281 | fn get_u128(&mut self) -> u128 { 282 | let mut buf = [0; 16]; 283 | self.copy_to_slice(&mut buf); 284 | T::read_u128(&buf) 285 | } 286 | 287 | fn get_i128(&mut self) -> i128 { 288 | let mut buf = [0; 16]; 289 | self.copy_to_slice(&mut buf); 290 | T::read_i128(&buf) 291 | } 292 | } 293 | 294 | impl BufExt for T {} 295 | 296 | 297 | /// Link field contains source block_hash if receiving, destination account if sending 298 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 299 | pub enum Link { 300 | Source(BlockHash), 301 | Destination(PublicKey), 302 | Unknown([u8; 32]) 303 | } 304 | 305 | // TODO: Process Link properly so that we can remove unkown type 306 | impl Link { 307 | pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { 308 | match *self { 309 | Link::Source(ref h) => h.as_bytes(), 310 | Link::Destination(ref k) => k.as_bytes(), 311 | Link::Unknown(ref b) => &b 312 | } 313 | } 314 | } 315 | 316 | #[derive(Debug, PartialEq, Eq, Clone)] 317 | pub enum BlockPayload { 318 | Send { 319 | previous: BlockHash, 320 | destination: PublicKey, 321 | /// The balance of the account *after* the send. 322 | balance: u128, 323 | }, 324 | Receive { 325 | previous: BlockHash, 326 | /// The block we're receiving. 327 | source: BlockHash, 328 | }, 329 | /// The first "receive" in an account chain. 330 | /// Creates the account, and sets the representative. 331 | Open { 332 | /// The block we're receiving. 333 | source: BlockHash, 334 | representative: PublicKey, 335 | account: PublicKey, 336 | }, 337 | /// Changes the representative for an account. 338 | Change { 339 | previous: BlockHash, 340 | representative: PublicKey, 341 | }, 342 | /// A universal transaction which contains the account state. 343 | State { 344 | account: PublicKey, 345 | previous: BlockHash, 346 | representative: PublicKey, 347 | balance: u128, 348 | link: Link, 349 | }, 350 | } 351 | 352 | impl BlockPayload { 353 | pub fn work_source(&self) -> InputHash { 354 | match *self { 355 | BlockPayload::Send { ref previous, .. } => previous.clone().into(), 356 | BlockPayload::Receive { ref previous, .. } => previous.clone().into(), 357 | BlockPayload::Open { ref account, .. } => InputHash::from_bytes(account.clone().to_bytes()).unwrap(), 358 | BlockPayload::Change { ref previous, .. } => previous.clone().into(), 359 | BlockPayload::State { ref previous, .. } => previous.clone().into(), 360 | } 361 | } 362 | 363 | pub fn serialize_bytes(&self, buf: &mut BytesMut) { 364 | match *self { 365 | BlockPayload::Send { 366 | ref previous, 367 | ref destination, 368 | ref balance, 369 | } => { 370 | buf.reserve(BlockKind::Send.size()); 371 | buf.put_slice(previous.as_bytes()); 372 | buf.put_slice(destination.as_bytes()); 373 | buf.put_u128::(*balance); 374 | } 375 | BlockPayload::Receive { 376 | ref previous, 377 | ref source, 378 | } => { 379 | buf.reserve(BlockKind::Receive.size()); 380 | buf.put_slice(previous.as_bytes()); 381 | buf.put_slice(source.as_bytes()); 382 | } 383 | BlockPayload::Open { 384 | ref source, 385 | ref representative, 386 | ref account, 387 | } => { 388 | buf.reserve(BlockKind::Open.size()); 389 | buf.put_slice(source.as_bytes()); 390 | buf.put_slice(representative.as_bytes()); 391 | buf.put_slice(account.as_bytes()); 392 | } 393 | BlockPayload::Change { 394 | ref previous, 395 | ref representative, 396 | } => { 397 | buf.reserve(BlockKind::Change.size()); 398 | buf.put_slice(previous.as_bytes()); 399 | buf.put_slice(representative.as_bytes()); 400 | } 401 | BlockPayload::State { 402 | ref account, 403 | ref previous, 404 | ref representative, 405 | ref balance, 406 | ref link, 407 | } => { 408 | buf.reserve(BlockKind::State.size()); 409 | buf.put_slice(account.as_bytes()); 410 | buf.put_slice(previous.as_bytes()); 411 | buf.put_slice(representative.as_bytes()); 412 | buf.put_u128::(*balance); 413 | buf.put_slice(link.as_bytes()); 414 | } 415 | } 416 | } 417 | 418 | pub fn deserialize_bytes(buf: &mut B, kind: BlockKind) -> Result { 419 | Ok(match kind { 420 | BlockKind::Send => { 421 | if buf.remaining() < BlockKind::Send.size() { 422 | bail!(ErrorKind::BlockPayloadLengthError(kind, buf.remaining())); 423 | } 424 | let mut temp_buf = [0u8; 32]; 425 | buf.copy_to_slice(&mut temp_buf); 426 | let previous = BlockHash::from_bytes(&temp_buf)?; 427 | buf.copy_to_slice(&mut temp_buf); 428 | let destination = PublicKey::from_bytes(&temp_buf)?; 429 | let balance = buf.get_u128::(); 430 | BlockPayload::Send { previous, destination, balance } 431 | } 432 | BlockKind::Receive => { 433 | if buf.remaining() < BlockKind::Receive.size() { 434 | bail!(ErrorKind::BlockPayloadLengthError(kind, buf.remaining())); 435 | } 436 | let mut temp_buf = [0u8; 32]; 437 | buf.copy_to_slice(&mut temp_buf); 438 | let previous = BlockHash::from_bytes(&temp_buf)?; 439 | buf.copy_to_slice(&mut temp_buf); 440 | let source = BlockHash::from_bytes(&temp_buf)?; 441 | BlockPayload::Receive { previous, source } 442 | } 443 | BlockKind::Open => { 444 | if buf.remaining() < BlockKind::Open.size() { 445 | bail!(ErrorKind::BlockPayloadLengthError(kind, buf.remaining())); 446 | } 447 | let mut temp_buf = [0u8; 32]; 448 | buf.copy_to_slice(&mut temp_buf); 449 | let source = BlockHash::from_bytes(&temp_buf)?; 450 | buf.copy_to_slice(&mut temp_buf); 451 | let representative = PublicKey::from_bytes(&temp_buf)?; 452 | buf.copy_to_slice(&mut temp_buf); 453 | let account = PublicKey::from_bytes(&temp_buf)?; 454 | BlockPayload::Open { source, representative, account } 455 | } 456 | BlockKind::Change => { 457 | if buf.remaining() < BlockKind::Change.size() { 458 | bail!(ErrorKind::BlockPayloadLengthError(kind, buf.remaining())); 459 | } 460 | let mut temp_buf = [0u8; 32]; 461 | buf.copy_to_slice(&mut temp_buf); 462 | let previous = BlockHash::from_bytes(&temp_buf)?; 463 | buf.copy_to_slice(&mut temp_buf); 464 | let representative = PublicKey::from_bytes(&temp_buf)?; 465 | BlockPayload::Change { previous, representative } 466 | } 467 | BlockKind::State => { 468 | if buf.remaining() < BlockKind::State.size() { 469 | bail!(ErrorKind::BlockPayloadLengthError(kind, buf.remaining())); 470 | } 471 | let mut temp_buf = [0u8; 32]; 472 | buf.copy_to_slice(&mut temp_buf); 473 | let account = PublicKey::from_bytes(&temp_buf)?; 474 | buf.copy_to_slice(&mut temp_buf); 475 | let previous = BlockHash::from_bytes(&temp_buf)?; 476 | buf.copy_to_slice(&mut temp_buf); 477 | let representative = PublicKey::from_bytes(&temp_buf)?; 478 | let balance = buf.get_u128::(); 479 | buf.copy_to_slice(&mut temp_buf); 480 | // TODO: Process link properly 481 | let link = Link::Unknown(temp_buf); 482 | BlockPayload::State { account, previous, representative, balance, link } 483 | } 484 | _ => bail!(ErrorKind::InvalidBlockPayloadKindError(kind)) 485 | }) 486 | } 487 | } 488 | 489 | impl Hash for BlockPayload { 490 | fn hash(&self, state: &mut H) { 491 | match *self { 492 | BlockPayload::Send { 493 | ref previous, 494 | ref destination, 495 | ref balance, 496 | } => { 497 | previous.hash(state); 498 | destination.hash(state); 499 | let mut buf = [0u8; 16]; 500 | BigEndian::write_u128(&mut buf, *balance); 501 | state.write(&buf); 502 | } 503 | BlockPayload::Receive { 504 | ref previous, 505 | ref source, 506 | } => { 507 | previous.hash(state); 508 | source.hash(state); 509 | } 510 | BlockPayload::Open { 511 | ref source, 512 | ref representative, 513 | ref account, 514 | } => { 515 | source.hash(state); 516 | representative.hash(state); 517 | account.hash(state); 518 | } 519 | BlockPayload::Change { 520 | ref previous, 521 | ref representative, 522 | } => { 523 | previous.hash(state); 524 | representative.hash(state); 525 | } 526 | BlockPayload::State { 527 | ref account, 528 | ref previous, 529 | ref representative, 530 | ref balance, 531 | ref link, 532 | } => { 533 | state.write(&[0u8; 31]); 534 | state.write(&[BlockKind::State as u8]); // block type code 535 | account.hash(state); 536 | previous.hash(state); 537 | representative.hash(state); 538 | let mut buf = [0u8; 16]; 539 | BigEndian::write_u128(&mut buf, *balance); 540 | state.write(&buf); 541 | state.write(link.as_bytes()); 542 | } 543 | } 544 | } 545 | } 546 | -------------------------------------------------------------------------------- /nano-lib-rs/src/error.rs: -------------------------------------------------------------------------------- 1 | error_chain!{ 2 | errors { 3 | /// Invalid character used in hex string 4 | InvalidHexCharacterError(pos: usize) { 5 | description("invalid character in hex") 6 | display("Invalid character in hex string at position {}", pos) 7 | } 8 | /// Attempted to set invalid work for a block 9 | InvalidWorkError { 10 | description("invalid work") 11 | display("Invalid Work") 12 | } 13 | /// Attempted to verify work for block with no work 14 | NoWorkError { 15 | description("attempted to verify work for block with no work") 16 | display("Attempted to verify work for block with no work") 17 | } 18 | /// Attempted to create a BlockHash with incorrect length 19 | BlockHashLengthError { 20 | description("attempted to create BlockHash with invalid length") 21 | display("Attempted to create BlockHash with invalid length") 22 | } 23 | /// Attempted to create a Signature with incorrect length 24 | SignatureLengthError { 25 | description("attempted to create Signature with invalid length") 26 | display("Attempted to create Signature with invalid length") 27 | } 28 | /// Attempted to create or parse a message with invalid header length 29 | MessageHeaderLengthError(len: usize) { 30 | description("Attempted to create or parse a message with invalid header length") 31 | display("Attempted to create message with buffer of length {} (must be at least 8)", len) 32 | } 33 | /// Error while parsing block 34 | BlockParseError(kind: BlockParseErrorKind) { 35 | description("Error while parsing block") 36 | display("Error while parsing block: {:?}", kind) 37 | } 38 | /// Attempted to create or parse a block with invalid data length for its kind 39 | BlockPayloadLengthError(kind: super::block::BlockKind, len: usize) { 40 | description("Attempted to create or parse a block with invalid data length for its kind") 41 | display("Attempted to create block of type {:?} with data length {} (should be {})", kind, len, kind.size()) 42 | } 43 | /// Attempted to deserialize a block payload for a type that does not have a payload 44 | InvalidBlockPayloadKindError(kind: super::block::BlockKind) { 45 | description("Attempted to deserialize a block payload for a type that does not have a payload") 46 | display("Attempted to deserialize a block payload for type {:?}, which does not have a payload", kind) 47 | } 48 | /// An error occurred while decoding an ed25519 key 49 | EdwardsDecodingError(err: String) { 50 | description("An error occurred while decoding an ed25519 key") 51 | display("{}", err) 52 | } 53 | /// Attempted to decode message with invalid magic number 54 | InvalidMagicNumber { 55 | description("Invalid magic number") 56 | display("Invalid magic number") 57 | } 58 | 59 | SeedLengthError(len: usize) { 60 | description("Invalid Seed Length") 61 | display("Invalid Seed Length! Expected 64 Got {}", len) 62 | } 63 | 64 | InvalidAddress { 65 | description("Invalid Address") 66 | display("Invalid Address") 67 | } 68 | 69 | InvalidAddressLength(len: usize) { 70 | description("Invalid Address Length") 71 | display("Invalid Address Length! Expected 64 Got {}", len) 72 | } 73 | } 74 | 75 | links { 76 | NanopowError(::nanopow_rs::error::Error, ::nanopow_rs::error::ErrorKind) #[doc = "An error occurred while generating Proof of Work."]; 77 | } 78 | 79 | foreign_links { 80 | DecodeError(::data_encoding::DecodeError); 81 | FormatError(::std::fmt::Error) #[doc = "A formatting error occured"]; 82 | BincodeError(::bincode::Error) #[doc = "An error occurred while serializing/deserializing binary data."]; 83 | IoError(::std::io::Error) #[doc = "An IO error occurred"]; 84 | } 85 | } 86 | 87 | #[derive(Debug, Copy, Clone)] 88 | pub enum BlockParseErrorKind { 89 | NoSignature, 90 | NoWork, 91 | } 92 | 93 | impl From<::ed25519_dalek::DecodingError> for Error { 94 | fn from(err: ::ed25519_dalek::DecodingError) -> Self { 95 | Self::from_kind(ErrorKind::EdwardsDecodingError(format!("{}", err))) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /nano-lib-rs/src/hash.rs: -------------------------------------------------------------------------------- 1 | use super::error::Result; 2 | use std::mem; 3 | use byteorder::{ByteOrder, LittleEndian}; 4 | 5 | pub trait Hasher { 6 | type Output; 7 | 8 | fn write(&mut self, bytes: &[u8]); 9 | fn write_u128(&mut self, i: u128) { 10 | let mut buf = [0u8; 64]; 11 | LittleEndian::write_u128(&mut buf, i); 12 | self.write(&buf); 13 | } 14 | fn finish(self) -> Result; 15 | } 16 | 17 | pub trait Hash { 18 | fn hash(&self, state: &mut H); 19 | fn hash_slice(data: &[Self], state: &mut H) 20 | where 21 | Self: Sized, 22 | { 23 | for piece in data { 24 | piece.hash(state); 25 | } 26 | } 27 | } 28 | 29 | impl Hash for u128 { 30 | fn hash(&self, state: &mut H) { 31 | state.write_u128(*self); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /nano-lib-rs/src/keys.rs: -------------------------------------------------------------------------------- 1 | use super::hash::{Hash, Hasher}; 2 | use blake2::{ 3 | digest::{Input, VariableOutput}, 4 | Blake2b 5 | }; 6 | use byteorder::{BigEndian, WriteBytesExt}; 7 | use data_encoding::{Encoding, HEXUPPER_PERMISSIVE, HEXLOWER_PERMISSIVE}; 8 | pub use ed25519_dalek::{ 9 | Keypair, 10 | PublicKey, 11 | SecretKey, 12 | Signature, 13 | PUBLIC_KEY_LENGTH, 14 | SECRET_KEY_LENGTH, 15 | SIGNATURE_LENGTH 16 | }; 17 | use nanopow_rs::InputHash; 18 | use error::*; 19 | use std::ops::{Deref, DerefMut}; 20 | 21 | impl Hash for PublicKey { 22 | fn hash(&self, state: &mut H) { 23 | state.write(self.as_bytes()) 24 | } 25 | } 26 | 27 | const XRB_ENCODING: Encoding = new_encoding! { 28 | symbols: "13456789abcdefghijkmnopqrstuwxyz", 29 | check_trailing_bits: false, 30 | }; 31 | 32 | #[derive(Debug, Clone)] 33 | pub struct Address(pub String); 34 | 35 | #[derive(Clone)] 36 | pub struct Seed(pub [u8; 64]); 37 | 38 | #[derive(Debug)] 39 | pub struct PrivateKey(SecretKey); 40 | 41 | impl Seed { 42 | pub fn from>(seed: T) -> Result { 43 | let seed = seed.as_ref(); 44 | if seed.len() != 64 { 45 | bail!(ErrorKind::SeedLengthError(seed.len())) 46 | } 47 | 48 | let seed = HEXUPPER_PERMISSIVE.decode(&seed).unwrap(); 49 | 50 | let mut seed_bytes = [0u8; 64]; 51 | seed_bytes.copy_from_slice(&seed); 52 | 53 | Ok(Seed(seed_bytes)) 54 | } 55 | } 56 | 57 | impl Deref for Seed { 58 | type Target = [u8; 64]; 59 | fn deref(&self) -> &Self::Target { 60 | &self.0 61 | } 62 | } 63 | 64 | impl DerefMut for Seed { 65 | fn deref_mut(&mut self) -> &mut [u8; 64] { 66 | &mut self.0 67 | } 68 | } 69 | 70 | impl Address { 71 | pub fn to_public_key(&self) -> Result { 72 | if let Some("xrb_") = self.0.get(..4) { 73 | if self.0.len() == 64 { 74 | let mut encoded_addr = String::from(self.0.get(4..56).unwrap()); 75 | encoded_addr.insert_str(0, "1111"); 76 | let checksum = self.0.get(56..).unwrap(); 77 | let pkey_bytes = XRB_ENCODING.decode(encoded_addr.as_bytes())?; 78 | let derived_checksum = XRB_ENCODING.encode(&compute_address_checksum(&pkey_bytes[3..])); 79 | if checksum != derived_checksum { 80 | bail!(ErrorKind::InvalidAddress) 81 | } 82 | return Ok(PublicKey::from_bytes(&pkey_bytes[3..])?) 83 | } 84 | bail!(ErrorKind::InvalidAddressLength(self.0.len())); 85 | } 86 | bail!(ErrorKind::InvalidAddress) 87 | } 88 | } 89 | 90 | /// the address checksum is the 5byte hash of the public key reversed 91 | /// 92 | pub fn compute_address_checksum(key_bytes: &[u8]) -> [u8; 5] { 93 | let mut blake = Blake2b::new(5).unwrap(); 94 | let mut buf = [0u8; 5]; 95 | blake.process(key_bytes); 96 | blake.variable_result(&mut buf).unwrap(); 97 | buf.reverse(); 98 | buf 99 | } 100 | 101 | #[derive(Debug, Clone)] 102 | pub struct Account { 103 | pub public_key: PublicKey, 104 | pub address: Address, 105 | } 106 | 107 | impl From for Address { 108 | fn from(key: PublicKey) -> Self { 109 | let mut p_key = key.to_bytes().to_vec(); 110 | let mut h = [0u8; 3].to_vec(); 111 | h.append(&mut p_key); 112 | let checksum = XRB_ENCODING.encode(&compute_address_checksum(key.as_bytes())); 113 | let address = { 114 | let encoded_addr = XRB_ENCODING.encode(&h); 115 | let mut addr = String::from("xrb_"); 116 | addr.push_str(encoded_addr.get(4..).unwrap()); 117 | addr.push_str(&checksum); 118 | addr 119 | }; 120 | 121 | Address(address) 122 | } 123 | } 124 | 125 | impl From for Account { 126 | fn from(key: PublicKey) -> Self { 127 | Account { 128 | public_key: key.clone(), 129 | address: key.into(), 130 | } 131 | } 132 | } 133 | 134 | impl From for Account { 135 | fn from(key: PrivateKey) -> Self { 136 | let public_key: PublicKey = key.into(); 137 | Account { 138 | public_key: public_key.clone(), 139 | address: public_key.into(), 140 | } 141 | } 142 | } 143 | 144 | impl From for PublicKey { 145 | fn from(key: PrivateKey) -> Self { 146 | PublicKey::from_secret::(&key) 147 | } 148 | } 149 | 150 | impl Deref for PrivateKey { 151 | type Target = SecretKey; 152 | fn deref(&self) -> &Self::Target { 153 | &self.0 154 | } 155 | } 156 | 157 | impl DerefMut for PrivateKey { 158 | fn deref_mut(&mut self) -> &mut SecretKey { 159 | &mut self.0 160 | } 161 | } 162 | 163 | impl PrivateKey { 164 | pub fn from_seed(seed: Seed, index: u32) -> PrivateKey { 165 | let mut blake = Blake2b::new(32).unwrap(); 166 | let mut index_buf = Vec::with_capacity(4); 167 | index_buf.write_u32::(index).unwrap(); 168 | blake.process(&*seed); 169 | blake.process(&index_buf); 170 | 171 | let mut buf = [0u8; 32]; 172 | blake.variable_result(&mut buf).unwrap(); 173 | PrivateKey(SecretKey::from_bytes(&buf).unwrap()) 174 | } 175 | } 176 | 177 | #[cfg(test)] 178 | mod tests { 179 | use super::*; 180 | 181 | #[test] 182 | fn can_generate_address_from_seed() { 183 | let seed = Seed::from_string("1234567890123456789012345678901234567890123456789012345678901234").unwrap(); 184 | 185 | // shamelessly copied from https://github.com/frankh/nano/blob/078a99b8e75bd239e13565312e06258164a781d5/address/address_test.go#L55-L59 186 | let expected_output = vec![ 187 | "xrb_3iwi45me3cgo9aza9wx5f7rder37hw11xtc1ek8psqxw5oxb8cujjad6qp9y", 188 | "xrb_3a9d1h6wt3zp8cqd6dhhgoyizmk1ciemqkrw97ysrphn7anm6xko1wxakaa1", 189 | "xrb_1dz36wby1azyjgh7t9nopjm3k5rduhmntercoz545my9s8nm7gcuthuq9fmq", 190 | "xrb_1fb7kaqaue49kf9w4mb9w3scuxipbdm3ez6ibnri4w8qexzg5f4r7on1dmxb", 191 | "xrb_3h9a64yqueuij1j9odt119r3ymm8n83wyyz7o9u7ram1tgfhsh1zqwjtzid9", 192 | ]; 193 | 194 | expected_output.into_iter().enumerate().for_each(|(index, address)| { 195 | let priv_key = PrivateKey::from_seed(seed.clone(), index as u32); 196 | let account: Account = priv_key.into(); 197 | 198 | assert_eq!(account.address.0, address) 199 | }) 200 | } 201 | 202 | #[test] 203 | fn can_convert_address_to_public_key() { 204 | let addr = Address("xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3".into()); 205 | let public_key = addr.to_public_key().unwrap(); 206 | let p_key_str = HEXLOWER_PERMISSIVE.encode(public_key.as_bytes()); 207 | // shamelessly copied from https://github.com/frankh/nano/blob/078a99b8e75bd239e13565312e06258164a781d5/address/address_test.go#L28-L30 208 | assert_eq!(p_key_str, "e89208dd038fbb269987689621d52292ae9c35941a7484756ecced92a65093ba") 209 | } 210 | 211 | #[test] 212 | fn can_validate_addresses() { 213 | let addresses = vec![ 214 | "xrb_38nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r8", 215 | "xrb_1awsn43we17c1oshdru4azeqjz9wii41dy8npubm4rg11so7dx3jtqgoeahy", 216 | "xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4", 217 | "xrb_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh", 218 | "xrb_3hd4ezdgsp15iemx7h81in7xz5tpxi43b6b41zn3qmwiuypankocw3awes5k", 219 | "xrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs" 220 | ]; 221 | 222 | addresses.into_iter().for_each(|addr| { 223 | Address(addr.into()).to_public_key().expect("Couldn't Validate Address"); 224 | }) 225 | } 226 | 227 | #[test] 228 | fn can_invalidate_addresses() { 229 | let addresses = vec![ 230 | "xrb_38nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r7", 231 | "xrc_38nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r8", 232 | "xrb38nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r8", 233 | "xrb8nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r8", 234 | "xrb_8nm8t5rimw6h6j7wyokbs8jiygzs7baoha4pqzhfw1k79npyr1km8w6y7r8", 235 | ]; 236 | 237 | let output = addresses.into_iter().map(|addr| { 238 | Address(addr.into()).to_public_key().is_err() 239 | }).collect::>(); 240 | 241 | assert_eq!(output, vec![true, true, true, true, true]) 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /nano-lib-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "1024"] 2 | #![allow(unused_imports)] 3 | #[macro_use] 4 | extern crate error_chain; 5 | 6 | extern crate byteorder; 7 | 8 | #[macro_use] 9 | extern crate log; 10 | 11 | #[macro_use] 12 | extern crate bitflags; 13 | 14 | extern crate bincode; 15 | extern crate serde; 16 | #[macro_use] 17 | extern crate serde_derive; 18 | extern crate serde_json; 19 | 20 | extern crate blake2; 21 | extern crate ed25519_dalek; 22 | 23 | extern crate bytes; 24 | extern crate data_encoding; 25 | #[macro_use] 26 | extern crate data_encoding_macro; 27 | 28 | #[cfg(test)] 29 | #[macro_use] 30 | extern crate pretty_assertions; 31 | 32 | extern crate nanopow_rs; 33 | 34 | extern crate tokio_io; 35 | 36 | #[macro_use] 37 | mod macros; 38 | 39 | pub mod block; 40 | pub mod keys; 41 | pub mod hash; 42 | pub mod error; 43 | pub mod message; 44 | -------------------------------------------------------------------------------- /nano-lib-rs/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Allows an enum to be serialized and deserialized into its value (stored as a single byte) 2 | /// This is how many of the header/metadata values are stored in Nano network `Message`s 3 | #[macro_export] 4 | macro_rules! enum_byte { 5 | ($name:ident { $($variant:ident = $value:expr, )* }) => { 6 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 7 | #[repr(u8)] 8 | pub enum $name { 9 | $($variant = $value,)* 10 | } 11 | 12 | impl $name { 13 | pub fn from_value(value: u8) -> Option { 14 | // Rust does not come with a simple way of converting a 15 | // number to an enum, so use a big `match`. 16 | match value { 17 | $( $value => Some($name::$variant), )* 18 | _ => None 19 | } 20 | } 21 | } 22 | 23 | impl ::serde::Serialize for $name { 24 | fn serialize(&self, serializer: S) -> ::std::result::Result 25 | where S: ::serde::Serializer 26 | { 27 | // Serialize the enum as a u8. 28 | serializer.serialize_u8(*self as u8) 29 | } 30 | } 31 | 32 | impl<'de> ::serde::Deserialize<'de> for $name { 33 | fn deserialize(deserializer: D) -> ::std::result::Result 34 | where D: ::serde::Deserializer<'de> 35 | { 36 | struct Visitor; 37 | 38 | impl<'de> ::serde::de::Visitor<'de> for Visitor { 39 | type Value = $name; 40 | 41 | fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 42 | formatter.write_str("byte") 43 | } 44 | 45 | fn visit_u8(self, value: u8) -> ::std::result::Result<$name, E> 46 | where E: ::serde::de::Error 47 | { 48 | match $name::from_value(value) { 49 | Some(v) => Ok(v), 50 | None => Err(E::custom( 51 | format!("unknown {} value: {}", 52 | stringify!($name), value))) 53 | } 54 | } 55 | } 56 | 57 | // Deserialize the enum from a u64. 58 | deserializer.deserialize_u8(Visitor) 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /nano-lib-rs/src/message.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Bytes, BytesMut, BufMut, Buf, IntoBuf, LittleEndian}; 2 | use bincode; 3 | use error::*; 4 | use block::{BlockKind, Block}; 5 | use std::net::{SocketAddrV6, Ipv6Addr}; 6 | use std::cmp; 7 | use keys::{PublicKey, Signature, SIGNATURE_LENGTH}; 8 | 9 | enum_byte!(MessageKind { 10 | Invalid = 0x00, 11 | NotAMessage = 0x01, 12 | KeepAlive = 0x02, 13 | Publish = 0x03, 14 | ConfirmReq = 0x04, 15 | ConfirmAck = 0x05, 16 | BulkPull = 0x06, 17 | BulkPush = 0x07, 18 | FrontierReq = 0x08, 19 | }); 20 | 21 | impl MessageKind { 22 | pub fn size(&self) -> Option { 23 | match *self { 24 | MessageKind::KeepAlive => Some(144), 25 | _ => None 26 | } 27 | } 28 | } 29 | 30 | pub const MAGIC_NUMBER: u8 = 0x52; 31 | 32 | enum_byte!(NetworkKind { 33 | Test = 0x41, // 'A' in ASCII 34 | Beta = 0x42, // 'B' in ASCII 35 | Live = 0x43, // 'C' in ASCII 36 | }); 37 | 38 | enum_byte!(Version { 39 | One = 0x01, 40 | Two = 0x02, 41 | Three = 0x03, 42 | Four = 0x04, 43 | Five = 0x05, 44 | Six = 0x06, 45 | Seven = 0x07, 46 | }); 47 | 48 | bitflags! { 49 | #[derive(Serialize, Deserialize)] 50 | pub struct Extensions: u8 { 51 | const IPV4_ONLY = 1; 52 | const BOOTSTRAP_NODE = 2; 53 | const NONE = 0; 54 | } 55 | } 56 | 57 | #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] 58 | pub struct MessageHeader { 59 | pub magic_number: u8, 60 | pub network: NetworkKind, 61 | pub version_max: Version, 62 | pub version_using: Version, 63 | pub version_min: Version, 64 | pub kind: MessageKind, 65 | pub extensions: Extensions, 66 | pub block_kind: BlockKind, 67 | } 68 | 69 | #[derive(Debug, Clone, PartialEq, Eq)] 70 | pub enum MessagePayload { 71 | Invalid, 72 | KeepAlive(Vec), 73 | Publish(Block), 74 | ConfirmReq(Block), 75 | ConfirmAck { 76 | public_key: PublicKey, 77 | signature: Signature, 78 | sequence: u64, 79 | block: Block, 80 | } 81 | } 82 | 83 | impl MessagePayload { 84 | pub fn serialize_bytes(&self) -> Bytes { 85 | match *self { 86 | MessagePayload::Invalid => { 87 | Bytes::with_capacity(0) 88 | }, 89 | MessagePayload::KeepAlive(ref peers) => { 90 | let mut buf = BytesMut::new(); 91 | buf.reserve(MessageKind::KeepAlive.size().unwrap()); 92 | // Official node will only accept exactly 8 peers 93 | let mut peers = peers.clone(); 94 | for _ in 0..(8 - cmp::min(peers.len(), 8)) { 95 | peers.push("[::]:0".parse().unwrap()); 96 | } 97 | for peer in &peers[..8] { 98 | buf.put_slice(&peer.ip().octets()[..]); 99 | buf.put_u16::(peer.port()); 100 | } 101 | Bytes::from(buf) 102 | }, 103 | MessagePayload::Publish(ref block) => { 104 | block.serialize_bytes() 105 | }, 106 | MessagePayload::ConfirmReq(ref block) => { 107 | block.serialize_bytes() 108 | }, 109 | MessagePayload::ConfirmAck { 110 | ref public_key, 111 | ref signature, 112 | ref sequence, 113 | ref block, 114 | } => { 115 | let mut buf = BytesMut::new(); 116 | buf.reserve(32 + 32 + 8 + block.kind.size()); 117 | buf.put_slice(public_key.as_bytes()); 118 | buf.put_slice(&signature.to_bytes()); 119 | buf.put_u64::(*sequence); 120 | let block_bytes = block.serialize_bytes(); 121 | buf.put(block_bytes); 122 | Bytes::from(buf) 123 | }, 124 | } 125 | } 126 | 127 | pub fn deserialize_bytes(header: MessageHeader, bytes: Bytes) -> Result { 128 | Ok(match header.kind { 129 | MessageKind::KeepAlive => { 130 | let peers: Vec = bytes.chunks(18).filter_map(|chunk| { 131 | if chunk.len() == 18 { 132 | let mut buf = chunk.into_buf(); 133 | let mut octets = [0u8; 16]; 134 | for i in 0..16 { 135 | octets[i] = buf.get_u8(); 136 | } 137 | Some(SocketAddrV6::new(Ipv6Addr::from(octets), buf.get_u16::(), 0, 0)) 138 | } else { 139 | None 140 | } 141 | }).collect(); 142 | if peers.len() > 0 { 143 | MessagePayload::KeepAlive(peers) 144 | } else { 145 | MessagePayload::Invalid 146 | } 147 | }, 148 | MessageKind::Publish => { 149 | MessagePayload::Publish(Block::deserialize_bytes(bytes, header.block_kind)?) 150 | }, 151 | MessageKind::ConfirmReq => { 152 | MessagePayload::ConfirmReq(Block::deserialize_bytes(bytes, header.block_kind)?) 153 | }, 154 | _ => { 155 | MessagePayload::Invalid 156 | } 157 | }) 158 | } 159 | } 160 | 161 | #[derive(Debug, Clone, PartialEq, Eq)] 162 | pub struct Message { 163 | pub header: MessageHeader, 164 | pub payload: MessagePayload, 165 | } 166 | 167 | impl Message { 168 | pub fn new(header: MessageHeader, payload: MessagePayload) -> Self { 169 | Message { 170 | header, 171 | payload 172 | } 173 | } 174 | 175 | pub fn serialize_bytes(&self) -> Result { 176 | let header_ser = bincode::serialize(&self.header)?; 177 | let data = self.payload.serialize_bytes(); 178 | let mut buf = BytesMut::with_capacity(header_ser.len() + data.len()); 179 | buf.put(header_ser); 180 | buf.put(data); 181 | Ok(Bytes::from(buf)) 182 | } 183 | 184 | pub fn deserialize_bytes(mut bytes: Bytes) -> Result { 185 | let len = bytes.len(); 186 | if bytes.len() < 8 { 187 | bail!(ErrorKind::MessageHeaderLengthError(len)); 188 | } 189 | let header_bytes = bytes.split_to(8); 190 | let header: MessageHeader = bincode::deserialize(&header_bytes)?; 191 | if header.magic_number as char != 'R' { 192 | bail!(ErrorKind::InvalidMagicNumber) 193 | } 194 | let payload = MessagePayload::deserialize_bytes(header, bytes)?; 195 | Ok(Message { 196 | header, 197 | payload 198 | }) 199 | } 200 | 201 | pub fn kind(&self) -> MessageKind { 202 | self.header.kind 203 | } 204 | } 205 | 206 | pub struct MessageBuilder { 207 | network: Option, 208 | version_max: Option, 209 | version_using: Option, 210 | version_min: Option, 211 | kind: MessageKind, 212 | block_kind: Option, 213 | extensions: Option, 214 | payload: Option, 215 | } 216 | 217 | impl MessageBuilder { 218 | pub fn new(kind: MessageKind) -> Self { 219 | MessageBuilder { 220 | network: None, 221 | version_max: None, 222 | version_using: None, 223 | version_min: None, 224 | kind: kind, 225 | extensions: None, 226 | block_kind: None, 227 | payload: None, 228 | } 229 | } 230 | 231 | pub fn with_network(mut self, network: NetworkKind) -> Self { 232 | self.network = Some(network); 233 | self 234 | } 235 | 236 | pub fn with_version_max(mut self, version: Version) -> Self { 237 | self.version_max = Some(version); 238 | self 239 | } 240 | 241 | pub fn with_version(mut self, version: Version) -> Self { 242 | self.version_using = Some(version); 243 | self 244 | } 245 | 246 | pub fn with_version_min(mut self, version: Version) -> Self { 247 | self.version_min = Some(version); 248 | self 249 | } 250 | 251 | pub fn with_extensions(mut self, extensions: Extensions) -> Self { 252 | self.extensions = Some(extensions); 253 | self 254 | } 255 | 256 | pub fn with_block_kind(mut self, block_kind: BlockKind) -> Self { 257 | self.block_kind = Some(block_kind); 258 | self 259 | } 260 | 261 | pub fn with_payload(mut self, payload: MessagePayload) -> Self { 262 | self.payload = Some(payload); 263 | self 264 | } 265 | 266 | pub fn build(self) -> Message { 267 | let header = MessageHeader { 268 | magic_number: MAGIC_NUMBER, 269 | network: self.network.unwrap_or(NetworkKind::Live), 270 | version_max: self.version_max.unwrap_or(Version::Seven), 271 | version_using: self.version_using.unwrap_or(Version::Seven), 272 | version_min: self.version_min.unwrap_or(Version::One), 273 | kind: self.kind, 274 | block_kind: self.block_kind.unwrap_or(BlockKind::Invalid), 275 | extensions: self.extensions.unwrap_or(Extensions::NONE), 276 | }; 277 | let payload = self.payload.unwrap_or(MessagePayload::Invalid); 278 | Message::new(header, payload) 279 | } 280 | } 281 | 282 | #[cfg(test)] 283 | mod tests { 284 | use super::*; 285 | use data_encoding::HEXUPPER; 286 | 287 | #[test] 288 | fn deserialize_message_header() { 289 | let test_cases = vec![ 290 | (b"5243070701020000", MAGIC_NUMBER, NetworkKind::Live, Version::Seven, Version::Seven, Version::One, MessageKind::KeepAlive, BlockKind::Invalid, Extensions::NONE), 291 | (b"5241060504000001", MAGIC_NUMBER, NetworkKind::Test, Version::Six, Version::Five, Version::Four, MessageKind::Invalid, BlockKind::NotABlock, Extensions::NONE), 292 | (b"5242030201010002", MAGIC_NUMBER, NetworkKind::Beta, Version::Three, Version::Two, Version::One, MessageKind::NotAMessage, BlockKind::Send, Extensions::NONE), 293 | (b"5243060601030003", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::Publish, BlockKind::Receive, Extensions::NONE), 294 | (b"5243060601040004", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::ConfirmReq, BlockKind::Open, Extensions::NONE), 295 | (b"5243060601050005", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::ConfirmAck, BlockKind::Change, Extensions::NONE), 296 | (b"5243060601060006", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::BulkPull, BlockKind::State, Extensions::NONE), 297 | (b"5243060601070006", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::BulkPush, BlockKind::State, Extensions::NONE), 298 | (b"5243060601080006", MAGIC_NUMBER, NetworkKind::Live, Version::Six, Version::Six, Version::One, MessageKind::FrontierReq, BlockKind::State, Extensions::NONE), 299 | ]; 300 | for (bytes, num, net, vmax, vuse, vmin, mkind, bkind, ext) in test_cases.into_iter() { 301 | let message_raw = Bytes::from(HEXUPPER.decode(bytes).unwrap()); 302 | let header: MessageHeader = bincode::deserialize(&message_raw).unwrap(); 303 | assert_eq!(header.magic_number, num); 304 | assert_eq!(header.network, net); 305 | assert_eq!(header.version_max, vmax); 306 | assert_eq!(header.version_using, vuse); 307 | assert_eq!(header.version_min, vmin); 308 | assert_eq!(header.kind, mkind); 309 | assert_eq!(header.block_kind, bkind); 310 | assert_eq!(header.extensions, ext); 311 | } 312 | } 313 | 314 | #[test] 315 | fn deserialize_keepalive() { 316 | let message_raw = Bytes::from(HEXUPPER.decode(b"524307070102000100000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B").unwrap()); 317 | let sock: SocketAddrV6 = "[::]:7075".parse().unwrap(); 318 | let message = Message::deserialize_bytes(message_raw.clone()).expect("should deserialize"); 319 | assert_eq!(message.payload, MessagePayload::KeepAlive(vec![sock.clone(); 8])); 320 | } 321 | 322 | #[test] 323 | fn serialize_keepalive() { 324 | let message_raw = Bytes::from(HEXUPPER.decode(b"524307070102000000000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B00000000000000000000000000000000A31B").unwrap()); 325 | let sock: SocketAddrV6 = "[::]:7075".parse().unwrap(); 326 | let message = MessageBuilder::new(MessageKind::KeepAlive) 327 | .with_payload(MessagePayload::KeepAlive(vec![sock.clone(); 8])) 328 | .build(); 329 | let message_ser = message.serialize_bytes().unwrap(); 330 | assert_eq!(&message_ser[..], &message_raw[..]); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /nanopow-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nanopow-rs" 3 | version = "0.4.0" 4 | authors = ["Gray Olson "] 5 | repository = "https://github.com/termhn/nano-rs" 6 | description = "Small Rust library to generate proof of work for the Nano cryptocurrency." 7 | license = "MIT" 8 | 9 | [dependencies] 10 | blake2 = "0.7" 11 | data-encoding = "2.1" 12 | crossbeam-utils = "0.2" 13 | crossbeam-channel = "0.1" 14 | num_cpus = "1.8" 15 | rand = "0.4" 16 | error-chain = "0.11" 17 | byteorder = "1.2" 18 | lazy_static = "1.0" 19 | 20 | [dev-dependencies] 21 | criterion = "0.2" 22 | 23 | [[bench]] 24 | name = "bench" 25 | harness = false 26 | -------------------------------------------------------------------------------- /nanopow-rs/README.md: -------------------------------------------------------------------------------- 1 | # Nanopow-rs 2 | 3 | Small Rust library to generate proof of work for the Nano cryptocurrency. Fully parallelized on the CPU. The goal is for this to be easily includable in web applications using WASM and stdweb. Currently, it works best as a native POW generation tool in Rust projects as well as in Node.js using [nanopow-rs-node](https://github.com/termhn/nanopow-rs-node). -------------------------------------------------------------------------------- /nanopow-rs/Web.toml: -------------------------------------------------------------------------------- 1 | default-target = "wasm32-unknown-unknown" -------------------------------------------------------------------------------- /nanopow-rs/benches/bench.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate nanopow_rs; 4 | 5 | use criterion::Criterion; 6 | 7 | use nanopow_rs::{generate_work, InputHash}; 8 | 9 | fn generate_unlimited(c: &mut Criterion) { 10 | c.bench_function("generate unlimited", |b| { 11 | let hash = InputHash::from_hex("47F694A96653EB497709490776E492EFBB88EBC5C4E95CC0B2C9DCAB1930C36B").unwrap(); 12 | b.iter(|| generate_work(&hash, None).unwrap()) 13 | }); 14 | } 15 | 16 | criterion_group!{ 17 | name = benches; 18 | config = Criterion::default().sample_size(10); 19 | targets = generate_unlimited 20 | } 21 | criterion_main!(benches); -------------------------------------------------------------------------------- /nanopow-rs/src/error.rs: -------------------------------------------------------------------------------- 1 | 2 | error_chain! { 3 | errors { 4 | /// Invalid character used in hex string 5 | InvalidHexCharacterError(pos: usize) { 6 | description("invalid character in hex") 7 | display("Invalid character in hex string at position {}", pos) 8 | } 9 | /// Attempted to create an InputHash with incorrect length 10 | HashLengthError { 11 | description("attempted to create InputHash with invalid length") 12 | display("Attempted to create InputHash with invalid length") 13 | } 14 | 15 | /// Attempted to create Work with incorrect length 16 | WorkLengthError { 17 | description("attempted to create Work with invalid length") 18 | display("Attempted to create Work with invalid length") 19 | } 20 | } 21 | 22 | foreign_links { 23 | FormatError(::std::fmt::Error) #[doc = "A formatting error occured"]; 24 | } 25 | } -------------------------------------------------------------------------------- /nanopow-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Small Rust library to generate proof of work for the Nano cryptocurrency. 2 | //! Fully parallelized on the CPU. The goal is for this to be easily includable 3 | //! in web applications as WASM using Parcel and stdweb. Currently, it works best 4 | //! as a native POW generation tool used in Rust and Node apps through 5 | //! [nanopow-rs-node](https://github.com/termhn/nanopow-rs-node) 6 | #![recursion_limit = "1024"] 7 | #![deny(missing_docs)] 8 | 9 | #[macro_use] 10 | extern crate error_chain; 11 | extern crate blake2; 12 | extern crate rand; 13 | extern crate data_encoding; 14 | extern crate crossbeam_utils; 15 | extern crate crossbeam_channel; 16 | extern crate num_cpus; 17 | extern crate byteorder; 18 | #[macro_use] 19 | extern crate lazy_static; 20 | 21 | use blake2::{Blake2b}; 22 | use blake2::digest::{Input, VariableOutput}; 23 | 24 | use data_encoding::{HEXLOWER, HEXUPPER}; 25 | 26 | use rand::{XorShiftRng, Rng, SeedableRng}; 27 | 28 | use byteorder::{ByteOrder, LittleEndian, BigEndian}; 29 | 30 | use std::fmt; 31 | 32 | /// Error types, using error-chain 33 | pub mod error; 34 | use error::*; 35 | 36 | const THRESHOLD_STR: &[u8] = b"ffffffc000000000"; 37 | 38 | lazy_static! { 39 | /// The network threshold 40 | pub static ref THRESHOLD: [u8; 8] = { 41 | let mut buf = [0u8; 8]; 42 | let _ = HEXLOWER.decode_mut(THRESHOLD_STR, &mut buf).unwrap(); 43 | buf 44 | }; 45 | } 46 | 47 | /// An 8 byte array used to represent the work value 48 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 49 | pub struct Work(pub u64); 50 | 51 | impl Work 52 | { 53 | /// Convert hexadecimal formatted data into a Work value. 54 | /// Hex value is expected to be encoded in *big-endian* byte order. 55 | pub fn from_hex>(s: T) -> Result { 56 | let bytes = s.as_ref(); 57 | if bytes.len() != 16 { 58 | bail!(ErrorKind::WorkLengthError); 59 | } 60 | let mut buf = [0u8; 8]; 61 | let _ = HEXLOWER.decode_mut(bytes, &mut buf) 62 | .map_err::(|e| ErrorKind::InvalidHexCharacterError(e.error.position).into())?; 63 | let work = BigEndian::read_u64(&buf); 64 | Ok(Work(work)) 65 | } 66 | } 67 | 68 | impl From for String { 69 | fn from(work: Work) -> Self { 70 | let mut buf = [0u8; 8]; 71 | BigEndian::write_u64(&mut buf, work.0); 72 | let string = HEXLOWER.encode(&buf); 73 | string 74 | } 75 | } 76 | 77 | impl fmt::Display for Work { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 79 | let string: String = (*self).into(); 80 | write!(f, "{}", string) 81 | } 82 | } 83 | 84 | 85 | /// A 32 byte array used to represent a valid input hash 86 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 87 | pub struct InputHash([u8; 32]); 88 | 89 | impl InputHash 90 | { 91 | /// Create Work from properly sized byte array 92 | pub fn new(bytes: [u8; 32]) -> Self { 93 | InputHash(bytes) 94 | } 95 | 96 | /// Convert hexadecimal formatted data into an InputHash 97 | pub fn from_hex>(s: T) -> Result { 98 | let bytes = s.as_ref(); 99 | if bytes.len() != 64 { 100 | bail!(ErrorKind::WorkLengthError); 101 | } 102 | let mut buf = [0u8; 32]; 103 | let _ = HEXUPPER.decode_mut(bytes, &mut buf) 104 | .map_err::(|e| ErrorKind::InvalidHexCharacterError(e.error.position).into())?; 105 | Ok(InputHash(buf)) 106 | } 107 | 108 | /// Create an InputHash from a raw byte slice 109 | pub fn from_bytes>(bytes: T) -> Result { 110 | let bytes = bytes.as_ref(); 111 | if bytes.len() != 32 { 112 | bail!(ErrorKind::HashLengthError); 113 | } 114 | let mut buf = [0u8; 32]; 115 | for i in 0..32 { 116 | buf[i] = bytes[i]; 117 | } 118 | Ok(InputHash(buf)) 119 | } 120 | 121 | /// View the hash as a byte slice 122 | pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] { 123 | &(self.0) 124 | } 125 | } 126 | 127 | impl AsRef<[u8]> for InputHash { 128 | fn as_ref(&self) -> &[u8] { 129 | &self.0 130 | } 131 | } 132 | 133 | impl From for String { 134 | fn from(hash: InputHash) -> Self { 135 | let string = HEXUPPER.encode(&hash.0); 136 | string 137 | } 138 | } 139 | 140 | impl fmt::Display for InputHash { 141 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 142 | let string: String = (*self).into(); 143 | write!(f, "{}", string) 144 | } 145 | } 146 | 147 | fn check_result_threshold(hash: &[u8; 8]) -> bool { 148 | (&hash).iter().rev().enumerate().fold(true, |acc, (i, &byte)| { 149 | acc && byte >= THRESHOLD[i] 150 | }) 151 | } 152 | 153 | fn hash_work_internal(work: &[u8], hash: &[u8]) -> [u8; 8] { 154 | let mut hasher = Blake2b::new(8).unwrap(); 155 | hasher.process(&work[..]); 156 | hasher.process(&hash[..]); 157 | let mut output = [0u8; 8]; 158 | hasher.variable_result(&mut output).unwrap(); 159 | output 160 | } 161 | 162 | /// Attempts to generate valid work for a given `InputHash` (usually a block hash or public key) 163 | /// with optional maximum iterations 164 | pub fn generate_work(hash: &InputHash, max_iters: Option) -> Option { 165 | let hash = hash.0; 166 | if let Some(w) = generate_work_internal(&hash[..], max_iters) { 167 | let work = LittleEndian::read_u64(&w); 168 | Some(Work(work)) 169 | } else { 170 | None 171 | } 172 | } 173 | 174 | fn generate_work_internal(hash: &[u8], max_iters: Option) -> Option<[u8; 8]> { 175 | let numcpus = num_cpus::get(); 176 | let (tx,rx) = crossbeam_channel::bounded::>(numcpus); 177 | let (donetx, donerx) = crossbeam_channel::bounded::(numcpus); 178 | let has_max_iters = max_iters.is_some(); 179 | let max_iters = max_iters.unwrap_or(numcpus as u64 * 2); 180 | crossbeam_utils::scoped::scope(|scope| { 181 | for _ in 0..numcpus { 182 | scope.spawn(|| { 183 | let mut rng: XorShiftRng = SeedableRng::from_seed(rand::random::<[u32; 4]>()); 184 | let mut work = [0u8; 8]; 185 | let mut iters = 0u64; 186 | let mut result_valid = false; 187 | let mut done = donerx.try_recv().unwrap_or(false); 188 | while !result_valid && !done && iters < max_iters/numcpus as u64 { 189 | work = rng.gen::<[u8; 8]>(); 190 | let output = hash_work_internal(&work[..], hash); 191 | result_valid = check_result_threshold(&output); 192 | if has_max_iters { 193 | iters += 1; 194 | } 195 | done = donerx.try_recv().unwrap_or(false); 196 | } 197 | if done { 198 | return; 199 | } 200 | if result_valid { 201 | let _ = tx.send(Some(work)).is_ok(); 202 | } else { 203 | let _ = tx.send(None).is_ok(); 204 | } 205 | }); 206 | } 207 | let mut res = rx.recv().unwrap(); 208 | let mut msgs_resvd = 0; 209 | while res.is_none() && msgs_resvd < numcpus-1 { 210 | res = rx.recv().unwrap(); 211 | msgs_resvd += 1; 212 | } 213 | for _ in 0..numcpus { 214 | donetx.send(true).unwrap(); 215 | } 216 | res 217 | }) 218 | } 219 | 220 | /// Checks if a given `Work` value is valid for a given `InputHash` (usually a block hash or public key) 221 | pub fn check_work(hash: &InputHash, work: &Work) -> bool { 222 | let hash = hash.0; 223 | let mut work_bytes = [0u8; 8]; 224 | LittleEndian::write_u64(&mut work_bytes, work.0); 225 | let value = hash_work_internal(&work_bytes, &hash); 226 | check_result_threshold(&value) 227 | } 228 | 229 | #[cfg(test)] 230 | mod tests { 231 | use super::*; 232 | 233 | #[test] 234 | fn creates_input_hash_from_hex_and_bytes() { 235 | let hex = String::from("8D3E5F07BFF7B7484CDCB392F47009F62997253D28BD98B94BCED95F03C4DA09"); 236 | let hash = InputHash::from_hex(&hex).unwrap(); 237 | let hash2 = InputHash::from_bytes(HEXUPPER.decode(hex.as_ref()).unwrap()).unwrap(); 238 | assert!(hash.0 == hash2.0); 239 | } 240 | 241 | #[test] 242 | fn creates_hex_string_from_input_hash() { 243 | let hex = String::from("8D3E5F07BFF7B7484CDCB392F47009F62997253D28BD98B94BCED95F03C4DA09"); 244 | let hash = InputHash::from_hex(&hex).unwrap(); 245 | let hash_conv: String = hash.into(); 246 | assert!(hex == hash_conv); 247 | } 248 | 249 | #[test] 250 | fn creates_work_from_hex() { 251 | let hex = String::from("4effb6b0cd5625e2"); 252 | let work = Work::from_hex(&hex).unwrap(); 253 | assert!(work.0 == 5692469324495070690); 254 | } 255 | 256 | #[test] 257 | fn creates_hex_string_from_work() { 258 | let hex = String::from("4effb6b0cd5625e2"); 259 | let work = Work::from_hex(&hex).unwrap(); 260 | let work_conv: String = work.into(); 261 | assert!(hex == work_conv); 262 | } 263 | 264 | #[test] 265 | fn validates_good_work() { 266 | let hash = InputHash::from_hex("8D3E5F07BFF7B7484CDCB392F47009F62997253D28BD98B94BCED95F03C4DA09").unwrap(); 267 | let work = Work::from_hex("4effb6b0cd5625e2").unwrap(); 268 | let valid = check_work(&hash, &work); 269 | assert!(valid); 270 | } 271 | 272 | #[test] 273 | fn does_not_validate_bad_work() { 274 | let hash = InputHash::from_hex("8D3E5F07BFF7B7484CDCB392F47009F62997253D28BD98B94BCED95F03C4DA09").unwrap(); 275 | let work = Work::from_hex("4effc680cd5625e2").unwrap(); 276 | let valid = check_work(&hash, &work); 277 | assert!(valid == false); 278 | } 279 | 280 | #[test] 281 | fn generates_valid_work() { 282 | let hash = InputHash::from_hex("47F694A96653EB497709490776E492EFBB88EBC5C4E95CC0B2C9DCAB1930C36B").unwrap(); 283 | let work = generate_work(&hash, None).unwrap(); 284 | let work_str: String = work.clone().into(); 285 | println!("generated work: {}", work_str); 286 | let valid = check_work(&hash, &work); 287 | assert!(valid); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | 2 | error_chain!{ 3 | errors{ 4 | /// A non recoverable error occurred while processing a stream 5 | #[allow(dead_code)] 6 | FatalStreamError { 7 | description("A non recoverable error occurred while processing a stream") 8 | display("A non recoverable error occurred while processing a stream") 9 | } 10 | /// An error occurred with a Tokio-timer timeout 11 | TokioTimeoutError(inner: String) { 12 | description("Error in Tokio Timeout") 13 | display("Error in tokio timeout: {}", inner) 14 | } 15 | } 16 | links{ 17 | NanoLibError(::nano_lib_rs::error::Error, ::nano_lib_rs::error::ErrorKind) #[doc = "An error occurred in nano-lib"]; 18 | } 19 | foreign_links{ 20 | FernInitError(::fern::InitError) #[doc = "An error occured while setting up fern"]; 21 | SetLoggerError(::log::SetLoggerError) #[doc = "An error occured while setting the logger"]; 22 | IoError(::std::io::Error) #[doc = "An IO error occurred"]; 23 | AddrParseError(::std::net::AddrParseError) #[doc = "An error occurred while parsing an address"]; 24 | TokioTimerError(::tokio_timer::TimerError) #[doc = "An error occurred in a tokio timer"]; 25 | } 26 | } 27 | 28 | impl From<::tokio_timer::TimeoutError> for Error { 29 | fn from(err: ::tokio_timer::TimeoutError) -> Self { 30 | Self::from_kind(ErrorKind::TokioTimeoutError(format!("{}", err))) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate tokio; 2 | extern crate tokio_io; 3 | extern crate tokio_timer; 4 | extern crate net2; 5 | #[macro_use] 6 | extern crate futures; 7 | 8 | extern crate data_encoding; 9 | 10 | extern crate nano_lib_rs; 11 | 12 | #[macro_use] 13 | extern crate log; 14 | extern crate fern; 15 | extern crate chrono; 16 | extern crate clap; 17 | 18 | #[macro_use] 19 | extern crate error_chain; 20 | 21 | extern crate bytes; 22 | 23 | extern crate rand; 24 | extern crate indexmap; 25 | 26 | mod error; 27 | mod net; 28 | mod utils; 29 | mod node; 30 | 31 | use error::*; 32 | use node::{NodeConfig}; 33 | 34 | use nano_lib_rs::message::NetworkKind; 35 | 36 | use std::net::{ToSocketAddrs, SocketAddr}; 37 | 38 | use futures::{Future}; 39 | 40 | use clap::{Arg, App}; 41 | 42 | fn run(network: NetworkKind) -> Result<()> { 43 | info!("Starting nano-rs!"); 44 | 45 | // TODO: Figure out why beta doesn't work and add test 46 | let port = match network { 47 | NetworkKind::Beta => 54000, 48 | _ => 7075 49 | }; 50 | 51 | let listen_addr = format!("[::]:{}", port).parse()?; 52 | let peers: Vec = format!("rai.raiblocks.net:{}", port).to_socket_addrs()?.collect(); 53 | if let None = peers.get(0) { 54 | return Err("Could not connect to initial peer".into()); 55 | } 56 | 57 | let config = NodeConfig { 58 | peers, 59 | network, 60 | listen_addr, 61 | }; 62 | 63 | let mut runtime = tokio::runtime::Runtime::new()?; 64 | let handle = runtime.handle().clone(); 65 | let node = node::run(config, &handle)?; 66 | 67 | runtime.spawn(node); 68 | runtime.shutdown_on_idle().wait().unwrap(); 69 | 70 | info!("Stopping nano-rs!"); 71 | Ok(()) 72 | } 73 | 74 | fn setup_logger(log_level: log::LevelFilter) -> Result<()> { 75 | use std::fs::create_dir; 76 | let base_path: &str = match create_dir("log") { 77 | Ok(_) => { 78 | "log/" 79 | }, 80 | Err(e) => { 81 | if e.kind() == std::io::ErrorKind::AlreadyExists { 82 | "log/" 83 | } else { 84 | "" 85 | } 86 | } 87 | }; 88 | fern::Dispatch::new() 89 | .format(|out, message, record| { 90 | out.finish(format_args!( 91 | "{}[{}][{}] {}", 92 | chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), 93 | record.target(), 94 | record.level(), 95 | message 96 | )) 97 | }) 98 | .level(log_level) 99 | .level_for("tokio_reactor", log::LevelFilter::Error) 100 | .chain(std::io::stderr()) 101 | .chain(fern::log_file(format!("{}nano-rs__{}.log", base_path, chrono::Local::now().format("%Y-%m-%d__%H-%M-%S")))?) 102 | .apply()?; 103 | Ok(()) 104 | } 105 | 106 | fn main() { 107 | let matches = App::new(env!("CARGO_PKG_NAME")) 108 | .version(env!("CARGO_PKG_VERSION")) 109 | .author("Gray Olson ") 110 | .about("An implementation of Nano in Rust using Tokio.") 111 | .arg(Arg::with_name("log-level") 112 | .short("l") 113 | .long("log-level") 114 | .value_name("LOG_LEVEL") 115 | .default_value("info") 116 | .possible_values(&["off", "error", "warn", "info", "debug", "trace"]) 117 | .case_insensitive(true) 118 | .help("Set logging level (default Info)")) 119 | .arg(Arg::with_name("network") 120 | .short("n") 121 | .long("network") 122 | .value_name("NET") 123 | .default_value("live") 124 | .possible_values(&["live", "beta", "test"]) 125 | .help("The nano network to connect to")) 126 | .get_matches(); 127 | 128 | let network = match matches.value_of("network").unwrap() { 129 | "live" => NetworkKind::Live, 130 | "beta" => NetworkKind::Beta, 131 | "test" => NetworkKind::Test, 132 | _ => unreachable!(), 133 | }; 134 | 135 | let log_level = match matches.value_of("log-level").unwrap() { 136 | "off" => log::LevelFilter::Off, 137 | "error" => log::LevelFilter::Error, 138 | "warn" => log::LevelFilter::Warn, 139 | "info" => log::LevelFilter::Info, 140 | "debug" => log::LevelFilter::Debug, 141 | "trace" => log::LevelFilter::Trace, 142 | _ => unreachable!(), 143 | }; 144 | 145 | // Setup logger 146 | if let Err(e) = setup_logger(log_level) { 147 | use std::io::Write; 148 | let stderr = &mut ::std::io::stderr(); 149 | let errmsg = "Error writing to stderr"; 150 | 151 | writeln!(stderr, "Error while initializing logger: {}", e).expect(errmsg); 152 | } 153 | 154 | // Run program and log errors from error-chain using logger 155 | if let Err(ref e) = run(network) { 156 | 157 | error!("Failed with error: {}", e); 158 | 159 | for e in e.iter().skip(1) { 160 | error!("Caused by: {}", e); 161 | } 162 | 163 | if let Some(backtrace) = e.backtrace() { 164 | error!("backtrace: {:?}", backtrace); 165 | } 166 | 167 | ::std::process::exit(1); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/net/codec.rs: -------------------------------------------------------------------------------- 1 | use bytes::{Bytes, BytesMut, BufMut}; 2 | use nano_lib_rs::message::{Message, MessageKind, MessageBuilder}; 3 | use tokio_io::codec::{Decoder, Encoder}; 4 | use error::*; 5 | 6 | pub struct MessageCodec(()); 7 | 8 | impl MessageCodec { 9 | pub fn new() -> Self { 10 | MessageCodec(()) 11 | } 12 | } 13 | 14 | impl Decoder for MessageCodec { 15 | type Item = Message; 16 | type Error = Error; 17 | 18 | fn decode(&mut self, buf: &mut BytesMut) -> Result> { 19 | trace!("Deserializing message: {:?}", &buf[..]); 20 | let bytes = Bytes::from(buf.take()); 21 | let message = match Message::deserialize_bytes(bytes) { 22 | Ok(m) => m, 23 | Err(e) => { 24 | error!("Error deserializing message: {}", e); 25 | MessageBuilder::new(MessageKind::Invalid).build() 26 | } 27 | }; 28 | Ok(Some(message)) 29 | } 30 | } 31 | 32 | impl Encoder for MessageCodec { 33 | type Item = Message; 34 | type Error = Error; 35 | 36 | fn encode(&mut self, msg: Message, dst: &mut BytesMut) -> Result<()> { 37 | let msg_ser = msg.serialize_bytes()?; 38 | trace!("Serialized message: {:?}", &msg_ser[..]); 39 | dst.reserve(msg_ser.len()); 40 | dst.put(msg_ser); 41 | Ok(()) 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | use bytes::{BytesMut}; 49 | use data_encoding::{HEXUPPER}; 50 | use std::net::SocketAddrV6; 51 | use nano_lib_rs::message::{MessagePayload}; 52 | use nano_lib_rs::block::{Block, BlockPayload, BlockKind, BlockHash, Work}; 53 | use nano_lib_rs::keys::Signature; 54 | 55 | #[test] 56 | fn encode_decode() { 57 | let addr: SocketAddrV6 = "[::]:7075".parse().unwrap(); 58 | let message = MessageBuilder::new(MessageKind::KeepAlive) 59 | .with_payload(MessagePayload::KeepAlive(vec![addr.clone(); 8])) 60 | .build(); 61 | let mut buf = BytesMut::new(); 62 | let mut a_codec = MessageCodec::new(); 63 | 64 | a_codec.encode(message.clone(), &mut buf).expect("should encode keepalive"); 65 | let res = a_codec.decode(&mut buf).unwrap().expect("should decode keepalive"); 66 | assert_eq!(message, res); 67 | 68 | let mut dummy_data = [82u8; 64]; 69 | for (i, byte) in dummy_data.iter_mut().enumerate() { 70 | if i%2 == 0 { 71 | *byte = 64; 72 | } 73 | if i%3 == 0 { 74 | *byte = 25; 75 | } 76 | } 77 | let block = Block::new( 78 | BlockKind::Receive, 79 | Some(BlockPayload::Receive { 80 | previous: BlockHash::from_bytes(&dummy_data[..32]).unwrap(), 81 | source: BlockHash::from_bytes(&dummy_data[..32]).unwrap(), 82 | }), 83 | Some(Signature::from_bytes(&dummy_data).unwrap()), 84 | Some(Work(0))); 85 | let message = MessageBuilder::new(MessageKind::Publish) 86 | .with_block_kind(BlockKind::Receive) 87 | .with_payload(MessagePayload::Publish(block)) 88 | .build(); 89 | 90 | a_codec.encode(message.clone(), &mut buf).expect("should encode publish"); 91 | let res = a_codec.decode(&mut buf).unwrap().expect("should decode publish"); 92 | assert_eq!(message, res); 93 | } 94 | 95 | #[test] 96 | fn decode_invalid_header() { 97 | let mut buf = BytesMut::new(); 98 | buf.extend_from_slice(b"\x52"); 99 | let mut codec = MessageCodec::new(); 100 | 101 | let res = codec.decode(&mut buf).unwrap().expect("should decode"); 102 | assert_eq!(res.kind(), MessageKind::Invalid); 103 | } 104 | 105 | #[test] 106 | fn decode_invalid_message_body() { 107 | let mut buf = BytesMut::from(HEXUPPER.decode(b"5243050501020000").unwrap()); 108 | buf.extend_from_slice(b"\x52"); 109 | let mut codec = MessageCodec::new(); 110 | 111 | let res = codec.decode(&mut buf).unwrap().expect("should decode"); 112 | assert_eq!(res.kind(), MessageKind::KeepAlive); 113 | assert_eq!(res.payload, MessagePayload::Invalid); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/net/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod codec; 2 | pub mod udp_framed; 3 | 4 | pub use self::udp_framed::UdpFramed; 5 | -------------------------------------------------------------------------------- /src/net/udp_framed.rs: -------------------------------------------------------------------------------- 1 | //! A custom version of tokio::net::UdpFramed that does not exit on send error and 2 | //! which contains a reference to a `State` object 3 | use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4}; 4 | 5 | use futures::{Async, Poll, Stream, Sink, StartSend, AsyncSink}; 6 | 7 | use tokio::net::UdpSocket; 8 | 9 | use tokio_io::codec::{Decoder, Encoder}; 10 | use bytes::{BytesMut, BufMut}; 11 | 12 | use std::sync::Arc; 13 | use node::state::State; 14 | use utils::to_ipv6; 15 | 16 | /// A unified `Stream` and `Sink` interface to an underlying `UdpSocket`, using 17 | /// the `Encoder` and `Decoder` traits to encode and decode frames. 18 | /// 19 | /// Raw UDP sockets work with datagrams, but higher-level code usually wants to 20 | /// batch these into meaningful chunks, called "frames". This method layers 21 | /// framing on top of this socket by using the `Encoder` and `Decoder` traits to 22 | /// handle encoding and decoding of messages frames. Note that the incoming and 23 | /// outgoing frame types may be distinct. 24 | /// 25 | /// This function returns a *single* object that is both `Stream` and `Sink`; 26 | /// grouping this into a single object is often useful for layering things which 27 | /// require both read and write access to the underlying object. 28 | /// 29 | /// If you want to work more directly with the streams and sink, consider 30 | /// calling `split` on the `UdpFramed` returned by this method, which will break 31 | /// them into separate objects, allowing them to interact more easily. 32 | #[must_use = "sinks do nothing unless polled"] 33 | #[derive(Debug)] 34 | pub struct UdpFramed { 35 | socket: UdpSocket, 36 | codec: C, 37 | rd: BytesMut, 38 | wr: BytesMut, 39 | out_addr: SocketAddr, 40 | flushed: bool, 41 | node_state: Arc, 42 | } 43 | 44 | impl Stream for UdpFramed { 45 | type Item = (C::Item, SocketAddr); 46 | type Error = C::Error; 47 | 48 | fn poll(&mut self) -> Poll, Self::Error> { 49 | self.rd.reserve(INITIAL_RD_CAPACITY); 50 | 51 | let (n, addr) = unsafe { 52 | // Read into the buffer without having to initialize the memory. 53 | let (n, addr) = try_ready!(self.socket.poll_recv_from(self.rd.bytes_mut())); 54 | self.rd.advance_mut(n); 55 | (n, addr) 56 | }; 57 | trace!("received {} bytes, decoding", n); 58 | let frame_res = self.codec.decode(&mut self.rd); 59 | self.rd.clear(); 60 | let frame = frame_res?; 61 | let result = frame.map(|frame| (frame, addr)); // frame -> (frame, addr) 62 | trace!("frame decoded from buffer"); 63 | Ok(Async::Ready(result)) 64 | } 65 | } 66 | 67 | impl Sink for UdpFramed { 68 | type SinkItem = (C::Item, SocketAddr); 69 | type SinkError = C::Error; 70 | 71 | fn start_send(&mut self, item: Self::SinkItem) -> StartSend { 72 | trace!("sending frame"); 73 | 74 | if !self.flushed { 75 | match try!(self.poll_complete()) { 76 | Async::Ready(()) => {}, 77 | Async::NotReady => return Ok(AsyncSink::NotReady(item)), 78 | } 79 | } 80 | 81 | let (frame, out_addr) = item; 82 | self.codec.encode(frame, &mut self.wr)?; 83 | self.out_addr = out_addr; 84 | self.flushed = false; 85 | trace!("frame encoded; length={}", self.wr.len()); 86 | 87 | Ok(AsyncSink::Ready) 88 | } 89 | 90 | fn poll_complete(&mut self) -> Poll<(), C::Error> { 91 | if self.flushed { 92 | return Ok(Async::Ready(())) 93 | } 94 | 95 | trace!("flushing frame; length={}", self.wr.len()); 96 | match self.socket.poll_send_to(&self.wr, &self.out_addr) { 97 | Ok(Async::NotReady) => { 98 | return Ok(Async::NotReady); 99 | }, 100 | Ok(Async::Ready(n)) => { 101 | trace!("written {}", n); 102 | 103 | let wrote_all = n == self.wr.len(); 104 | self.wr.clear(); 105 | self.flushed = true; 106 | 107 | if !wrote_all { 108 | debug!("Failed to write entire datagram to socket; Wrote: {} expected: {}", n, self.wr.len()); 109 | } 110 | }, 111 | Err(e) => { 112 | if e.kind() == ::std::io::ErrorKind::WouldBlock { 113 | return Ok(Async::NotReady); 114 | } 115 | debug!("Error sending frame: {:?}, removing peer: {}", e, self.out_addr); 116 | self.node_state.remove_peer(to_ipv6(self.out_addr)); 117 | } 118 | } 119 | Ok(Async::Ready(())) 120 | } 121 | 122 | fn close(&mut self) -> Poll<(), C::Error> { 123 | try_ready!(self.poll_complete()); 124 | Ok(().into()) 125 | } 126 | } 127 | 128 | const INITIAL_RD_CAPACITY: usize = 64 * 1024; 129 | const INITIAL_WR_CAPACITY: usize = 8 * 1024; 130 | 131 | impl UdpFramed { 132 | /// Create a new `UdpFramed` backed by the given socket and codec. 133 | /// 134 | /// See struct level documention for more details. 135 | pub fn new(socket: UdpSocket, codec: C, state: Arc) -> UdpFramed { 136 | UdpFramed { 137 | socket: socket, 138 | codec: codec, 139 | out_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)), 140 | rd: BytesMut::with_capacity(INITIAL_RD_CAPACITY), 141 | wr: BytesMut::with_capacity(INITIAL_WR_CAPACITY), 142 | flushed: true, 143 | node_state: state, 144 | } 145 | } 146 | 147 | /// Returns a reference to the underlying I/O stream wrapped by `Framed`. 148 | /// 149 | /// # Note 150 | /// 151 | /// Care should be taken to not tamper with the underlying stream of data 152 | /// coming in as it may corrupt the stream of frames otherwise being worked 153 | /// with. 154 | #[allow(dead_code)] 155 | pub fn get_ref(&self) -> &UdpSocket { 156 | &self.socket 157 | } 158 | 159 | /// Returns a mutable reference to the underlying I/O stream wrapped by 160 | /// `Framed`. 161 | /// 162 | /// # Note 163 | /// 164 | /// Care should be taken to not tamper with the underlying stream of data 165 | /// coming in as it may corrupt the stream of frames otherwise being worked 166 | /// with. 167 | #[allow(dead_code)] 168 | pub fn get_mut(&mut self) -> &mut UdpSocket { 169 | &mut self.socket 170 | } 171 | 172 | /// Consumes the `Framed`, returning its underlying I/O stream. 173 | #[allow(dead_code)] 174 | pub fn into_inner(self) -> UdpSocket { 175 | self.socket 176 | } 177 | } -------------------------------------------------------------------------------- /src/node/handler.rs: -------------------------------------------------------------------------------- 1 | use nano_lib_rs::message::{MessageBuilder, Message, MessageKind, MessagePayload}; 2 | 3 | use node::State; 4 | use error::*; 5 | use utils::check_addr; 6 | 7 | use std::net::{SocketAddrV6, SocketAddr}; 8 | use std::sync::Arc; 9 | 10 | use futures::{stream, Stream}; 11 | 12 | pub fn keepalive(msg: Message, _src: SocketAddrV6, state: Arc) 13 | -> Box + Send> 14 | { 15 | if let MessagePayload::KeepAlive(peer_addrs) = msg.payload { 16 | let send_peers = state.random_peers(8); 17 | let msg = MessageBuilder::new(MessageKind::KeepAlive) 18 | .with_payload(MessagePayload::KeepAlive(send_peers)) 19 | .build(); 20 | let to_send = peer_addrs.into_iter() 21 | .filter_map(move |peer_addr| { 22 | if check_addr(peer_addr) { 23 | Some((msg.clone(), SocketAddr::V6(peer_addr))) 24 | } else { 25 | None 26 | } 27 | }); 28 | let count = state.peer_count(); 29 | debug!("Added peers, new peer count: {}", count); 30 | Box::new(stream::iter_ok(to_send)) 31 | } else { 32 | debug!("Malformed Keepalive, no peers added!"); 33 | Box::new(stream::empty()) 34 | } 35 | } 36 | 37 | pub fn publish(mut msg: Message, _src: SocketAddrV6, _state: Arc) 38 | -> Box + Send> 39 | { 40 | if let MessagePayload::Publish(ref mut block) = msg.payload { 41 | let hash = match block.hash(false) { 42 | Ok(hash) => hash.into(), 43 | Err(e) => format!("Error calculating hash for block: {}", e), 44 | }; 45 | let valid = if block.verify_work().unwrap_or(false) { "valid" } else { "INVALID" }; 46 | info!("Got {:?} block with hash {}; Work {}", block.kind, hash, valid); 47 | Box::new(stream::empty()) 48 | } else { 49 | debug!("Malformed Publish, ignoring."); 50 | Box::new(stream::empty()) 51 | } 52 | } 53 | 54 | pub fn confirm_req(mut msg: Message, _src: SocketAddrV6, _state: Arc) 55 | -> Box + Send> 56 | { 57 | 58 | if let MessagePayload::ConfirmReq(ref mut block) = msg.payload { 59 | let hash = match block.hash(false) { 60 | Ok(hash) => hash.into(), 61 | Err(e) => format!("Error calculating hash for block: {}", e), 62 | }; 63 | let valid = if block.verify_work().unwrap_or(false) { "valid" } else { "INVALID" }; 64 | info!("Got {:?} block with hash {}; Work {}", block.kind, hash, valid); 65 | Box::new(stream::empty()) 66 | } else { 67 | debug!("Malformed ConfirmReq, ignoring."); 68 | Box::new(stream::empty()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/node/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod handler; 2 | pub mod state; 3 | use self::state::{State, PeerInfo}; 4 | 5 | use net::codec::MessageCodec; 6 | use net::{UdpFramed}; 7 | 8 | use nano_lib_rs::message::{MessageBuilder, Message, MessageKind, MessagePayload, NetworkKind}; 9 | use nano_lib_rs; 10 | 11 | use tokio; 12 | use tokio::prelude::*; 13 | use tokio::net::{UdpSocket}; 14 | use futures::{self, Future}; 15 | use futures::sync::mpsc; 16 | 17 | use std::net::{SocketAddr, SocketAddrV6}; 18 | use net2::UdpBuilder; 19 | use std::sync::{Arc}; 20 | 21 | use tokio_timer::{Timer, TimerError}; 22 | use std::time::{Duration}; 23 | 24 | use indexmap::IndexMap; 25 | 26 | use error::*; 27 | 28 | use utils::{log_errors, to_ipv6}; 29 | 30 | const KEEPALIVE_INTERVAL: u64 = 60; 31 | const KEEPALIVE_CUTOFF: u64 = KEEPALIVE_INTERVAL * 5; 32 | 33 | const PEER_PRUNE_INTERVAL: u64 = KEEPALIVE_INTERVAL * 2; 34 | 35 | fn process_messages(network: NetworkKind, state: Arc, stream: S) -> impl Stream 36 | where S: Stream 37 | { 38 | stream.map(move |(msg, src_addr)| -> Box + Send> { 39 | if network == msg.header.network { 40 | let state = state.clone(); 41 | let kind = msg.kind(); 42 | let src_addr_v6 = to_ipv6(src_addr); 43 | let _ = state.add_or_update_peer(src_addr_v6, true); 44 | debug!("Received message of kind: {:?} from {}", kind, src_addr); 45 | match kind { 46 | MessageKind::KeepAlive => handler::keepalive(msg, src_addr_v6, state.clone()), 47 | MessageKind::Publish => handler::publish(msg, src_addr_v6, state.clone()), 48 | MessageKind::ConfirmReq => handler::confirm_req(msg, src_addr_v6, state.clone()), 49 | _ => Box::new(stream::empty()) 50 | } 51 | } else { 52 | debug!("Received message from {:?} network, ignoring...", msg.header.network); 53 | Box::new(stream::empty()) 54 | } 55 | }) 56 | .flatten() 57 | } 58 | 59 | fn send_keepalives(state: Arc, timer: &Timer) -> impl Stream { 60 | stream::once(Ok(())) 61 | .chain(timer.interval(Duration::from_secs(KEEPALIVE_INTERVAL))) 62 | .map(move |_| { 63 | let state = state.clone(); 64 | let count = state.peer_count(); 65 | debug!("Sending keepalives to peers. Current peer count: {}", count); 66 | let peers = state.peers.read().unwrap().clone(); 67 | let inner_state = state.clone(); 68 | stream::iter_ok::<_, Error>(peers.into_iter()).map(move |(addr, _)| { 69 | let send_peers = inner_state.random_peers(8); 70 | let msg = MessageBuilder::new(MessageKind::KeepAlive) 71 | .with_payload(MessagePayload::KeepAlive(send_peers)) 72 | .build(); 73 | (msg, SocketAddr::V6(addr)) 74 | }) 75 | }) 76 | .flatten() 77 | } 78 | 79 | fn prune_peers(state: Arc, timer: &Timer) -> impl Future { 80 | timer.interval(Duration::from_secs(PEER_PRUNE_INTERVAL)) 81 | .for_each(move |_| { 82 | let state = state.clone(); 83 | let count = state.prune_peers(); 84 | debug!("Pruned {} inactive peers. Current peer count: {}", count, state.peer_count()); 85 | futures::future::ok(()) 86 | }) 87 | } 88 | 89 | pub struct NodeConfig { 90 | pub peers: Vec, 91 | pub listen_addr: SocketAddr, 92 | pub network: NetworkKind, 93 | } 94 | 95 | 96 | pub fn run(config: NodeConfig, handle: &tokio::reactor::Handle) -> Result> { 97 | let socket_std = UdpBuilder::new_v6()? 98 | .only_v6(false)? 99 | .bind(&config.listen_addr)?; 100 | let socket = UdpSocket::from_std(socket_std, handle)?; 101 | 102 | info!("Listening on: {}", socket.local_addr()?); 103 | 104 | let initial_peers: IndexMap = config.peers.into_iter() 105 | .map(|addr| { 106 | (to_ipv6(addr), PeerInfo::default()) 107 | }).collect(); 108 | 109 | let state = Arc::new(State::new(initial_peers)); 110 | 111 | let (sink, stream) = UdpFramed::new(socket, MessageCodec::new(), state.clone()).split(); 112 | 113 | let message_processor = process_messages(config.network, state.clone(), stream); 114 | 115 | let timer = Timer::default(); 116 | let keepalive_handler = send_keepalives(state.clone(), &timer); 117 | let peer_prune_handler = prune_peers(state.clone(), &timer); 118 | 119 | let (sock_send, sock_recv) = mpsc::channel::<(nano_lib_rs::message::Message, SocketAddr)>(2048); 120 | let process_send = sock_send.clone(); 121 | let keepalive_send = sock_send.clone(); 122 | 123 | Ok(futures::future::lazy(||{ 124 | tokio::spawn( 125 | process_send 126 | .sink_map_err(|e| error!("Fatal error sending messages: {:?}", e)) 127 | .send_all(log_errors(message_processor) 128 | .map_err(|e| error!("Fatal error processing keepalives: {:?}", e))) 129 | .map(|_| ()) 130 | ); 131 | 132 | tokio::spawn( 133 | keepalive_send 134 | .sink_map_err(|e| error!("Fatal sending keepalive: {:?}", e)) 135 | .send_all(log_errors(keepalive_handler) 136 | .map_err(|e| error!("Fatal error processing keepalives: {:?}", e))) 137 | .map(|_| ()) 138 | ); 139 | 140 | tokio::spawn( 141 | peer_prune_handler 142 | .map_err(|e| error!("Error pruning peers: {}", e)) 143 | ); 144 | 145 | tokio::spawn(sink 146 | .sink_map_err(|e| error!("Fatal error sending message: {:?}", e)) 147 | .send_all(sock_recv) 148 | .map(|_| ())); 149 | 150 | Ok(()) 151 | })) 152 | } -------------------------------------------------------------------------------- /src/node/state.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{RwLock}; 2 | use std::time::{Instant, Duration}; 3 | use std::net::{SocketAddrV6}; 4 | use indexmap::IndexMap; 5 | use indexmap::map::{Entry}; 6 | use rand::{self, Rng}; 7 | 8 | use utils::{check_addr}; 9 | use super::KEEPALIVE_CUTOFF; 10 | 11 | #[derive(Clone, Copy, Debug)] 12 | pub struct PeerInfo { 13 | last_seen: Instant 14 | } 15 | 16 | impl Default for PeerInfo { 17 | fn default() -> Self { 18 | PeerInfo { 19 | last_seen: Instant::now() 20 | } 21 | } 22 | } 23 | 24 | type Peers = IndexMap; 25 | 26 | #[derive(Debug)] 27 | pub struct State { 28 | pub peers: RwLock, 29 | pub inactive_peers: RwLock, 30 | } 31 | 32 | impl State { 33 | pub fn new(initial_peers: Peers) -> Self { 34 | State { 35 | peers: RwLock::new(initial_peers), 36 | inactive_peers: RwLock::new(IndexMap::new()), 37 | } 38 | } 39 | 40 | pub fn peer_count(&self) -> usize { 41 | self.peers.read().unwrap().len() 42 | } 43 | 44 | pub fn add_or_update_peer(&self, peer: SocketAddrV6, force: bool) -> bool { 45 | if !force { 46 | let inactive_map = self.inactive_peers.read().unwrap(); 47 | if let Some(_) = inactive_map.get(&peer) { 48 | return false; 49 | } 50 | } 51 | let mut inactive_map = self.inactive_peers.write().unwrap(); 52 | let mut map = self.peers.write().unwrap(); 53 | if let Entry::Occupied(entry) = inactive_map.entry(peer) { 54 | map.insert(peer, *entry.get()); 55 | entry.remove(); 56 | } 57 | match map.entry(peer) { 58 | Entry::Occupied(mut entry) => { 59 | entry.get_mut().last_seen = Instant::now(); 60 | false 61 | }, 62 | Entry::Vacant(entry) => { 63 | if check_addr(peer) { 64 | entry.insert(PeerInfo::default()); 65 | true 66 | } else { 67 | false 68 | } 69 | } 70 | } 71 | } 72 | 73 | pub fn prune_peers(&self) -> usize { 74 | let mut inactive_map = self.inactive_peers.write().unwrap(); 75 | let mut map = self.peers.write().unwrap(); 76 | let mut to_prune = Vec::new(); 77 | for (addr, info) in map.iter() { 78 | if Instant::now() - info.last_seen > Duration::from_secs(KEEPALIVE_CUTOFF) { 79 | to_prune.push(*addr); 80 | inactive_map.insert(*addr, *info); 81 | } 82 | } 83 | for addr in to_prune.iter() { 84 | map.remove(addr); 85 | } 86 | to_prune.len() 87 | } 88 | 89 | pub fn remove_peer(&self, peer: SocketAddrV6) { 90 | let mut map = self.peers.write().unwrap(); 91 | if let Entry::Occupied(entry) = map.entry(peer) { 92 | entry.remove(); 93 | } 94 | } 95 | 96 | pub fn random_peers(&self, n: usize) -> Vec { 97 | let mut rng = rand::thread_rng(); 98 | let peers = self.peers.read().unwrap(); 99 | (0..n).into_iter().map(|_| { 100 | let idx = rng.gen_range::(0, peers.len()); 101 | peers.get_index(idx).unwrap().0.clone() 102 | }).collect() 103 | } 104 | } -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use futures::{Async, Stream}; 4 | use error::*; 5 | 6 | #[macro_export] 7 | macro_rules! default_addr { 8 | () => { ::std::net::SocketAddrV6::new(::std::net::Ipv6Addr::from([0u8; 16]), 0, 0, 0) }; 9 | } 10 | 11 | pub struct LogErrors { 12 | inner: S, 13 | phantom_item: PhantomData 14 | } 15 | 16 | impl Stream for LogErrors 17 | where S: Stream 18 | { 19 | type Item = S::Item; 20 | type Error = Error; 21 | 22 | fn poll(&mut self) -> Result>> { 23 | loop { 24 | match self.inner.poll() { 25 | Ok(x) => return Ok(x), 26 | Err(e) => { 27 | if let ErrorKind::FatalStreamError = *e.kind() { 28 | return Err(e); 29 | } else { 30 | error!("Non-fatal error in stream: {:?}", e); 31 | } 32 | }, 33 | } 34 | } 35 | } 36 | } 37 | 38 | pub fn log_errors(stream: S) -> LogErrors 39 | where S: Stream 40 | { 41 | LogErrors { 42 | inner: stream, 43 | phantom_item: PhantomData, 44 | } 45 | } 46 | 47 | const IPV4_RESERVED_ADDRESSES: &[(u32, u32)] = &[ 48 | (0x00000000, 0x00ffffff), // rfc 1700 49 | (0x7f000000, 0x7fffffff), // loopback 50 | (0xc0000200, 0xc00002ff), // rfc 5737 51 | (0xc6336400, 0xc63364ff), // rfc 5737 52 | (0xcb007100, 0xcb0071ff), // rfc 5737 53 | (0xe0000000, 0xefffffff), // multicast 54 | (0xf0000000, 0xffffffff), // rfc 6890 55 | ]; 56 | 57 | use std::net::{SocketAddr, SocketAddrV6}; 58 | 59 | pub fn check_addr(addr: SocketAddrV6) -> bool { 60 | let ip = addr.ip().clone(); 61 | if ip.octets().iter().all(|&x| x == 0) { 62 | return false; 63 | } 64 | if addr.port() == 0 { 65 | return false; 66 | } 67 | if ip.is_unspecified() || ip.is_loopback() || ip.is_multicast() 68 | { 69 | return false; 70 | } 71 | if let Some(ip) = ip.to_ipv4() { 72 | let ip: u32 = ip.into(); 73 | for &(start, end) in IPV4_RESERVED_ADDRESSES.iter() { 74 | if ip >= start && ip <= end { 75 | return false; 76 | } 77 | } 78 | } 79 | true 80 | } 81 | 82 | pub fn to_ipv6(addr: SocketAddr) -> SocketAddrV6 { 83 | match addr { 84 | SocketAddr::V4(addr) => SocketAddrV6::new(addr.ip().to_ipv6_mapped(), addr.port(), 0, 0), 85 | SocketAddr::V6(addr) => addr, 86 | } 87 | } 88 | 89 | --------------------------------------------------------------------------------