diff --git a/.travis.yml b/.travis.yml index e7d5d389d..34693f96a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,8 +43,6 @@ jobs: # Add clippy and rustfmt: - rustup update - rustup component add clippy rustfmt - # Install capnp: - - ci/pre/capnp.sh # Check formatting: - cargo fmt --all -- --check # Run clippy check: @@ -69,8 +67,6 @@ jobs: os: osx osx_image: xcode11.3 # Solution due to https://travis-ci.community/t/homebrew-syntax-error/5623 : - before_script: - - HOMEBREW_NO_AUTO_UPDATE=1 brew install capnp script: - cargo test @@ -81,7 +77,6 @@ jobs: # See: https://travis-ci.community/t/windows-instances-hanging-before-install/250/25 filter_secrets: false before_script: - - choco install capnproto - rustup target add x86_64-pc-windows-msvc script: - cargo test diff --git a/Cargo.lock b/Cargo.lock index c7f323b43..87a377f7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,2324 +4,2405 @@ name = "addr2line" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" dependencies = [ - "gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli", ] [[package]] name = "aead" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672ba0d12aaf256aabcdab516ebed87b5cec4b602316d7a3035fac9b7a9567b" dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.2", ] [[package]] name = "aho-corasick" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "async-std" version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d" dependencies = [ - "async-task 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kv-log-macro 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smol 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-timer", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "smol", + "wasm-bindgen-futures", ] [[package]] name = "async-task" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3" [[package]] name = "atomicwrites" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a2baf2feb820299c53c7ad1cc4f5914a220a1cb76d7ce321d2522a94b54651f" dependencies = [ - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "nix", + "tempdir", + "winapi", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi", ] [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" dependencies = [ - "addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line", + "cfg-if", + "libc", + "object", + "rustc-demangle", ] [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "safemem", ] [[package]] name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "base64" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitmaps" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "blake2b_simd" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ - "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "arrayvec", + "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", ] [[package]] name = "block-buffer" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcf92448676f82bb7a334c58bbce8b0d43580fb5362a9d608b18879d12a3d31" dependencies = [ - "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.14.2", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools", ] [[package]] name = "blocking" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d17efb70ce4421e351d61aafd90c16a20fb5bfe339fcdc32a86816280e62ce0" dependencies = [ - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "waker-fn 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-util", + "once_cell", + "parking", + "waker-fn", ] [[package]] name = "bstr" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "memchr", + "regex-automata", + "serde", ] [[package]] name = "bumpalo" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] name = "cache-padded" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "capnp" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "capnpc" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "capnp 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "24508e28c677875c380c20f4d28124fab6f8ed4ef929a1397d7b1a31e92f1005" [[package]] name = "cc" version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chacha20" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c0f07ac275808b7bf9a39f2fd013aae1498be83632814c8c4e0bd53f2dc58" dependencies = [ - "stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher", + "zeroize", ] [[package]] name = "chacha20poly1305" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b0c90556d8e3fec7cf18d84a2f53d27b21288f2fe481b830fadcf809e48205" dependencies = [ - "aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aead", + "chacha20", + "poly1305", + "stream-cipher", + "zeroize", ] [[package]] name = "chrono" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" dependencies = [ - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", + "num-traits", + "time", ] [[package]] name = "clap" version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" dependencies = [ - "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "cluFlock" version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc9a586f6ea37daf0f6154727023de0a52342bb84cf692451f09b2690e1034be" dependencies = [ - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", ] [[package]] name = "colored" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "lazy_static", + "winapi", ] [[package]] name = "concurrent-queue" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83c06aff61f2d899eb87c379df3cbf7876f14471dcab474e0b6dc90ab96c080" dependencies = [ - "cache-padded 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cache-padded", ] [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "cfg-if", + "lazy_static", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-mac" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.2", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" +dependencies = [ + "generic-array 0.14.2", + "subtle", ] [[package]] name = "csv" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" dependencies = [ - "bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] name = "csv-core" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "curve25519-dalek" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", ] [[package]] name = "derive_more" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "rustc_version", + "syn 0.15.44", ] [[package]] name = "derive_more" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "proc-macro2 0.4.30", + "quote 0.6.13", + "regex", + "rustc_version", + "syn 0.15.44", ] [[package]] name = "derive_more" version = "0.99.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3", ] [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.2", ] [[package]] name = "dirs" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" dependencies = [ - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_users", + "winapi", ] [[package]] name = "ed25519-dalek" version = "1.0.0-pre.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" dependencies = [ - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clear_on_drop", + "curve25519-dalek", + "rand 0.7.3", + "sha2 0.8.2", ] [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "regex", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90eb1dec02087df472ab9f0db65f27edaa654a746830042688bcc2eaf68090f" [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] name = "futures-channel" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-core" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-executor" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", + "num_cpus", ] [[package]] name = "futures-io" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "futures-sink" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" dependencies = [ - "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", ] [[package]] name = "futures-timer" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" dependencies = [ - "gloo-timers 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "send_wrapper 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gloo-timers", + "send_wrapper", ] [[package]] name = "futures-util" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] name = "futures_codec" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8859feb7140742ed1a2a85a07941100ad2b5f98a421b353931d718a34144d1" dependencies = [ - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "memchr", + "pin-project", ] [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] name = "generic-array" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" dependencies = [ - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", + "version_check", ] [[package]] name = "getrandom" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] name = "gimli" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" [[package]] name = "gloo-timers" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" dependencies = [ - "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "heck" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" dependencies = [ - "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation", ] [[package]] name = "hermit-abi" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "hkdf" version = "0.9.0-alpha.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e551da9a76291df932270bc2b100d0571588eaa9ef77af7bceee80dba9ace3ad" dependencies = [ - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.9.0", + "hmac 0.8.0", ] [[package]] name = "hmac" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87b580bd66811cc2324a27f3587de707cacf7525b96dca8122f7493e6cce0da" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" dependencies = [ - "crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac 0.9.1", + "digest 0.9.0", ] [[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error", ] [[package]] name = "im" version = "14.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7" dependencies = [ - "bitmaps 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xoshiro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "sized-chunks 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitmaps", + "quickcheck", + "rand_core 0.5.1", + "rand_xoshiro", + "serde", + "sized-chunks", + "typenum", + "version_check", ] [[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177" dependencies = [ - "wasm-bindgen 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen", ] [[package]] name = "kv-log-macro" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff57d6d215f7ca7eb35a9a64d656ba4d9d2bef114d741dc08048e75e2f5d418" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" + +[[package]] +name = "libsqlite3-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e704a02bcaecd4a08b93a23f6be59d0bd79cd161e0963e9499165a0a35df7bd" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map", ] [[package]] name = "memchr" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cc", + "cfg-if", + "libc", + "void", ] [[package]] name = "num-bigint" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-integer", + "num-traits", ] [[package]] name = "num-integer" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-traits" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ - "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "object" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" [[package]] name = "offset-app" version = "0.1.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-app-client 0.1.0", - "offset-common 0.1.0", - "offset-connection 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-net 0.1.0", - "offset-proto 0.1.0", - "offset-signature 0.1.0", - "offset-timer 0.1.0", - "simple_logger 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.14.1", + "futures", + "log", + "offset-app-client", + "offset-common", + "offset-connection", + "offset-crypto", + "offset-identity", + "offset-net", + "offset-proto", + "offset-signature", + "offset-timer", + "simple_logger", ] [[package]] name = "offset-app-client" version = "0.1.0" dependencies = [ - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-proto 0.1.0", + "futures", + "offset-common", + "offset-proto", ] [[package]] name = "offset-app-server" version = "0.1.0" dependencies = [ - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "im 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-timer 0.1.0", + "futures", + "im", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-proto", + "offset-timer", ] [[package]] name = "offset-bin" version = "0.1.0" dependencies = [ - "async-std 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.7 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-connection 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-identity 0.1.0", - "offset-index-server 0.1.0", - "offset-net 0.1.0", - "offset-node 0.1.0", - "offset-proto 0.1.0", - "offset-relay 0.1.0", - "offset-timer 0.1.0", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "offset-capnp-conv" -version = "0.1.0" -dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "capnpc 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-capnp-conv-derive 0.1.0", - "pretty_env_logger 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "offset-capnp-conv-derive" -version = "0.1.0" -dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "base64 0.10.1", + "derive_more 0.99.7", + "env_logger 0.6.2", + "futures", + "log", + "offset-common", + "offset-connection", + "offset-crypto", + "offset-database", + "offset-identity", + "offset-index-server", + "offset-net", + "offset-node", + "offset-proto", + "offset-relay", + "offset-timer", + "serde", + "structopt", + "tempfile", ] [[package]] name = "offset-channeler" version = "0.1.0" dependencies = [ - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-relay 0.1.0", - "offset-timer 0.1.0", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-proto", + "offset-relay", + "offset-timer", ] [[package]] name = "offset-common" version = "0.1.0" dependencies = [ - "backtrace 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "base64 0.10.1", + "byteorder", + "bytes", + "futures", + "log", + "paste 1.0.1", + "quickcheck", + "serde", + "uint", ] [[package]] name = "offset-connection" version = "0.1.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-keepalive 0.1.0", - "offset-net 0.1.0", - "offset-proto 0.1.0", - "offset-secure-channel 0.1.0", - "offset-signature 0.1.0", - "offset-timer 0.1.0", - "offset-version 0.1.0", - "simple_logger 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.14.1", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-keepalive", + "offset-net", + "offset-proto", + "offset-secure-channel", + "offset-signature", + "offset-timer", + "offset-version", + "simple_logger", ] [[package]] name = "offset-crypto" version = "0.1.0" dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-proto 0.1.0", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "byteorder", + "bytes", + "chacha20poly1305", + "derive_more 0.14.1", + "ed25519-dalek", + "hkdf", + "hmac 0.9.0", + "offset-common", + "offset-proto", + "quickcheck", + "quickcheck_derive", + "rand 0.7.3", + "rand_core 0.5.1", + "serde", + "sha2 0.9.0", + "x25519-dalek", ] [[package]] name = "offset-database" version = "0.1.0" dependencies = [ - "atomicwrites 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "im 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atomicwrites", + "base64 0.9.3", + "futures", + "im", + "log", + "offset-common", + "offset-proto", + "rusqlite", + "serde", + "serde_json", + "tempfile", ] [[package]] name = "offset-funder" version = "0.1.0" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "im 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-signature 0.1.0", - "pretty_env_logger 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "derive_more 0.15.0", + "futures", + "im", + "log", + "offset-common", + "offset-crypto", + "offset-database", + "offset-identity", + "offset-proto", + "offset-signature", + "paste 1.0.1", + "pretty_env_logger", + "quickcheck", + "quickcheck_derive", + "quickcheck_macros 0.9.1", + "rand 0.7.3", + "serde", ] [[package]] name = "offset-identity" version = "0.1.0" dependencies = [ - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-proto 0.1.0", + "futures", + "offset-common", + "offset-crypto", + "offset-proto", ] [[package]] name = "offset-index-client" version = "0.1.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-signature 0.1.0", - "offset-timer 0.1.0", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-database", + "offset-identity", + "offset-proto", + "offset-signature", + "offset-timer", + "quickcheck", + "quickcheck_derive", + "quickcheck_macros 0.9.1", + "rand 0.7.3", + "serde", ] [[package]] name = "offset-index-server" version = "0.1.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-signature 0.1.0", - "offset-timer 0.1.0", + "env_logger 0.6.2", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-proto", + "offset-signature", + "offset-timer", ] [[package]] name = "offset-keepalive" version = "0.1.0" dependencies = [ - "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-proto 0.1.0", - "offset-timer 0.1.0", + "derive_more 0.15.0", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-proto", + "offset-timer", ] [[package]] name = "offset-lockfile" version = "0.1.0" dependencies = [ - "cluFlock 1.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cluFlock", + "tempfile", ] [[package]] name = "offset-mutual-from" version = "0.1.0" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", ] [[package]] name = "offset-net" version = "0.1.0" dependencies = [ - "async-std 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-proto 0.1.0", + "async-std", + "bytes", + "env_logger 0.6.2", + "futures", + "futures_codec", + "log", + "offset-common", + "offset-proto", ] [[package]] name = "offset-node" version = "0.1.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-app-server 0.1.0", - "offset-channeler 0.1.0", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-funder 0.1.0", - "offset-identity 0.1.0", - "offset-index-client 0.1.0", - "offset-net 0.1.0", - "offset-proto 0.1.0", - "offset-relay 0.1.0", - "offset-signature 0.1.0", - "offset-timer 0.1.0", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.14.1", + "futures", + "log", + "offset-app-server", + "offset-channeler", + "offset-common", + "offset-crypto", + "offset-database", + "offset-funder", + "offset-identity", + "offset-index-client", + "offset-net", + "offset-proto", + "offset-relay", + "offset-signature", + "offset-timer", + "quickcheck", + "quickcheck_derive", + "quickcheck_macros 0.9.1", + "rand 0.7.3", + "serde", ] [[package]] name = "offset-proto" version = "0.1.0" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "capnpc 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "im 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-capnp-conv 0.1.0", - "offset-common 0.1.0", - "offset-mutual-from 0.1.0", - "paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1", + "byteorder", + "bytes", + "derive_more 0.14.1", + "im", + "num-bigint", + "num-traits", + "offset-common", + "offset-mutual-from", + "paste 0.1.16", + "quickcheck", + "quickcheck_derive", + "rand 0.7.3", + "serde", + "serde_json", + "tempfile", ] [[package]] name = "offset-relay" version = "0.1.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-timer 0.1.0", + "derive_more 0.14.1", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-proto", + "offset-timer", ] [[package]] name = "offset-route" version = "0.1.0" dependencies = [ - "num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-proto 0.1.0", + "num-bigint", + "num-traits", + "offset-common", + "offset-crypto", + "offset-proto", ] [[package]] name = "offset-secure-channel" version = "0.1.0" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-identity 0.1.0", - "offset-proto 0.1.0", - "offset-timer 0.1.0", - "pretty_env_logger 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "derive_more 0.15.0", + "futures", + "log", + "offset-common", + "offset-crypto", + "offset-identity", + "offset-proto", + "offset-timer", + "pretty_env_logger", ] [[package]] name = "offset-signature" version = "0.1.0" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", - "offset-crypto 0.1.0", - "offset-proto 0.1.0", + "byteorder", + "offset-common", + "offset-crypto", + "offset-proto", ] [[package]] name = "offset-stcompact" version = "0.1.0" dependencies = [ - "async-std 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "derive_more 0.99.7 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-app 0.1.0", - "offset-app-client 0.1.0", - "offset-common 0.1.0", - "offset-connection 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-identity 0.1.0", - "offset-lockfile 0.1.0", - "offset-net 0.1.0", - "offset-node 0.1.0", - "offset-proto 0.1.0", - "offset-route 0.1.0", - "offset-timer 0.1.0", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std", + "base64 0.10.1", + "derive_more 0.99.7", + "env_logger 0.6.2", + "futures", + "log", + "offset-app", + "offset-app-client", + "offset-common", + "offset-connection", + "offset-crypto", + "offset-database", + "offset-identity", + "offset-lockfile", + "offset-net", + "offset-node", + "offset-proto", + "offset-route", + "offset-timer", + "quickcheck", + "quickcheck_derive", + "quickcheck_macros 0.9.1", + "rand 0.7.3", + "serde", + "serde_json", + "structopt", + "tempfile", ] [[package]] name = "offset-stctrl" version = "0.1.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-app 0.1.0", - "offset-mutual-from 0.1.0", - "offset-route 0.1.0", - "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.14.1", + "env_logger 0.6.2", + "futures", + "log", + "offset-app", + "offset-mutual-from", + "offset-route", + "prettytable-rs", + "quickcheck", + "quickcheck_derive", + "quickcheck_macros 0.9.1", + "rand 0.7.3", + "serde", + "structopt", + "tempfile", ] [[package]] name = "offset-test" version = "0.1.0" dependencies = [ - "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-app 0.1.0", - "offset-app-client 0.1.0", - "offset-bin 0.1.0", - "offset-common 0.1.0", - "offset-connection 0.1.0", - "offset-crypto 0.1.0", - "offset-database 0.1.0", - "offset-funder 0.1.0", - "offset-identity 0.1.0", - "offset-index-server 0.1.0", - "offset-net 0.1.0", - "offset-node 0.1.0", - "offset-proto 0.1.0", - "offset-relay 0.1.0", - "offset-stcompact 0.1.0", - "offset-stctrl 0.1.0", - "offset-timer 0.1.0", - "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.6.2", + "futures", + "log", + "offset-app", + "offset-app-client", + "offset-bin", + "offset-common", + "offset-connection", + "offset-crypto", + "offset-database", + "offset-funder", + "offset-identity", + "offset-index-server", + "offset-net", + "offset-node", + "offset-proto", + "offset-relay", + "offset-stcompact", + "offset-stctrl", + "offset-timer", + "quickcheck", + "quickcheck_macros 0.8.0", + "rand 0.7.3", + "serde_json", + "tempfile", ] [[package]] name = "offset-timer" version = "0.1.0" dependencies = [ - "async-std 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", + "async-std", + "bytes", + "futures", + "log", + "offset-common", ] [[package]] name = "offset-version" version = "0.1.0" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "offset-common 0.1.0", + "byteorder", + "futures", + "log", + "offset-common", ] [[package]] name = "once_cell" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "parking" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4029bc3504a62d92e42f30b9095fdef73b8a0b2a06aa41ce2935143b05a1a06" [[package]] name = "paste" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" dependencies = [ - "paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl", + "proc-macro-hack", ] +[[package]] +name = "paste" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0520af26d4cf99643dbbe093a61507922b57232d9978d8491fdc8f7b44573c8c" + [[package]] name = "paste-impl" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" dependencies = [ - "proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "pin-project" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75373ff9037d112bb19bc61333a06a159eaeb217660dcfbea7d88e1db823919" dependencies = [ - "pin-project-internal 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b4b44893d3c370407a1d6a5cfde7c41ae0478e31c516c85f67eb3adc51be6d" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "pin-project-lite" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" [[package]] name = "poly1305" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b42192ab143ed7619bf888a7f9c6733a9a2153b218e2cd557cfdb52fbf9bb1" dependencies = [ - "universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "universal-hash", ] [[package]] name = "ppv-lite86" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "pretty_env_logger" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed8d1e63042e889b85228620629b51c011d380eed2c7e0015f8a644def280c28" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "chrono", + "env_logger 0.5.13", + "log", ] [[package]] name = "prettytable-rs" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "csv", + "encode_unicode", + "lazy_static", + "term", + "unicode-width", ] [[package]] name = "proc-macro-hack" version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "proc-macro-nested" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de" [[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0", ] [[package]] name = "proc-macro2" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quickcheck" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" dependencies = [ - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.7.1", + "log", + "rand 0.7.3", + "rand_core 0.5.1", ] [[package]] name = "quickcheck_derive" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695566b1208e5a3e5a613d3326fcdb78203e1493c81514bfb4f4460ce0694deb" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "syn 1.0.31", + "synstructure", ] [[package]] name = "quickcheck_macros" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dfc1c4a1e048f5cc7d36a4c4118dfcf31d217c79f4b9a61bad65d68185752c" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "quote" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", ] [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_xoshiro" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "redox_users" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" dependencies = [ - "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "redox_syscall", + "rust-argon2", ] [[package]] name = "regex" version = "1.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" dependencies = [ - "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-automata" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "regex-syntax" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" [[package]] name = "remove_dir_all" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" +dependencies = [ + "winapi", +] + +[[package]] +name = "rusqlite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45d0fd62e1df63d254714e6cb40d0a0e82e7a1623e7a27f679d851af092ae58b" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "libsqlite3-sys", + "lru-cache", + "memchr", + "smallvec", + "time", ] [[package]] name = "rust-argon2" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" dependencies = [ - "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.11.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "scoped-tls" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "send_wrapper" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" [[package]] name = "serde" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" dependencies = [ - "serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", ] [[package]] name = "serde_json" version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" dependencies = [ - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha2" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug", ] [[package]] name = "sha2" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72377440080fd008550fe9b441e854e43318db116f90181eef92e9ae9aedab48" dependencies = [ - "block-buffer 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.8.0", + "digest 0.9.0", + "fake-simd", + "opaque-debug", ] [[package]] name = "simple_logger" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0c4611f32f4c2bac73754f22dca1f57e6c1945e0590dae4e5f2a077b92367" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "chrono", + "colored", + "log", + "winapi", ] [[package]] name = "sized-chunks" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718" dependencies = [ - "bitmaps 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitmaps", + "typenum", ] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + +[[package]] +name = "smallvec" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" [[package]] name = "smol" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" dependencies = [ - "async-task 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blocking 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "concurrent-queue 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fastrand 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", - "wepoll-sys-stjepang 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task", + "blocking", + "concurrent-queue", + "fastrand", + "futures-io", + "futures-util", + "libc", + "once_cell", + "scoped-tls", + "slab", + "socket2", + "wepoll-sys-stjepang", + "winapi", ] [[package]] name = "socket2" version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "redox_syscall", + "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stream-cipher" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f8ed9974042b8c3672ff3030a69fcc03b74c47c3d1ecb7755e8a3626011e88" dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.2", ] [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" dependencies = [ - "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", - "structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", + "clap", + "structopt-derive", ] [[package]] name = "structopt-derive" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" dependencies = [ - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "heck", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", ] [[package]] name = "subtle" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" [[package]] name = "syn" version = "0.15.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] name = "syn" version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "synstructure" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "unicode-xid 0.2.0", ] [[package]] name = "synstructure" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", + "unicode-xid 0.2.0", ] [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "remove_dir_all", ] [[package]] name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "rand 0.7.3", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] name = "term" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" dependencies = [ - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "dirs", + "winapi", ] [[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "time" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", ] [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "uint" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +dependencies = [ + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] [[package]] name = "unicode-segmentation" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" [[package]] name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "universal-hash" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ - "generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.14.2", + "subtle", ] +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "waker-fn" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101" dependencies = [ - "bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3" dependencies = [ - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7", + "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", + "wasm-bindgen-backend", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" [[package]] name = "web-sys" version = "0.3.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17" dependencies = [ - "js-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "wasm-bindgen", ] [[package]] name = "wepoll-sys-stjepang" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694" dependencies = [ - "cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "x25519-dalek" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" dependencies = [ - "curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", ] [[package]] name = "zeroize" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" dependencies = [ - "zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" -"checksum aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3672ba0d12aaf256aabcdab516ebed87b5cec4b602316d7a3035fac9b7a9567b" -"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" -"checksum async-std 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00d68a33ebc8b57800847d00787307f84a562224a14db069b0acefe4c2abbf5d" -"checksum async-task 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c17772156ef2829aadc587461c7753af20b7e8db1529bc66855add962a3b35d3" -"checksum atomicwrites 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6a2baf2feb820299c53c7ad1cc4f5914a220a1cb76d7ce321d2522a94b54651f" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.48 (registry+https://github.com/rust-lang/crates.io-index)" = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum bitmaps 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" -"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-buffer 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dbcf92448676f82bb7a334c58bbce8b0d43580fb5362a9d608b18879d12a3d31" -"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -"checksum blocking 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9d17efb70ce4421e351d61aafd90c16a20fb5bfe339fcdc32a86816280e62ce0" -"checksum bstr 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931" -"checksum bumpalo 3.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" -"checksum cache-padded 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24508e28c677875c380c20f4d28124fab6f8ed4ef929a1397d7b1a31e92f1005" -"checksum capnp 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b867c15d8ff93c4d81b69c89280840f877331ef2a1fccbaf947afecc68b51a9e" -"checksum capnpc 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2afedfc194b01c6804ad0a10c7139024b99ee3df6a39bb09bdf759067ababff" -"checksum cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "086c0f07ac275808b7bf9a39f2fd013aae1498be83632814c8c4e0bd53f2dc58" -"checksum chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18b0c90556d8e3fec7cf18d84a2f53d27b21288f2fe481b830fadcf809e48205" -"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" -"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" -"checksum cluFlock 1.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dc9a586f6ea37daf0f6154727023de0a52342bb84cf692451f09b2690e1034be" -"checksum colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -"checksum concurrent-queue 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f83c06aff61f2d899eb87c379df3cbf7876f14471dcab474e0b6dc90ab96c080" -"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" -"checksum csv-core 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -"checksum curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" -"checksum derive_more 0.99.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2127768764f1556535c01b5326ef94bd60ff08dcfbdc544d53e69ed155610f5d" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum digest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -"checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" -"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fastrand 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b90eb1dec02087df472ab9f0db65f27edaa654a746830042688bcc2eaf68090f" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" -"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" -"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" -"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" -"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" -"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" -"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" -"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" -"checksum futures-timer 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" -"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" -"checksum futures_codec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8859feb7140742ed1a2a85a07941100ad2b5f98a421b353931d718a34144d1" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" -"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -"checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" -"checksum gloo-timers 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" -"checksum hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e551da9a76291df932270bc2b100d0571588eaa9ef77af7bceee80dba9ace3ad" -"checksum hmac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b87b580bd66811cc2324a27f3587de707cacf7525b96dca8122f7493e6cce0da" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum im 14.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7" -"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -"checksum js-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177" -"checksum kv-log-macro 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4ff57d6d215f7ca7eb35a9a64d656ba4d9d2bef114d741dc08048e75e2f5d418" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -"checksum num-bigint 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -"checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" -"checksum once_cell 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parking 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c4029bc3504a62d92e42f30b9095fdef73b8a0b2a06aa41ce2935143b05a1a06" -"checksum paste 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" -"checksum paste-impl 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" -"checksum pin-project 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e75373ff9037d112bb19bc61333a06a159eaeb217660dcfbea7d88e1db823919" -"checksum pin-project-internal 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "10b4b44893d3c370407a1d6a5cfde7c41ae0478e31c516c85f67eb3adc51be6d" -"checksum pin-project-lite 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" -"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -"checksum poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b42192ab143ed7619bf888a7f9c6733a9a2153b218e2cd557cfdb52fbf9bb1" -"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" -"checksum pretty_env_logger 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8d1e63042e889b85228620629b51c011d380eed2c7e0015f8a644def280c28" -"checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" -"checksum proc-macro-hack 0.5.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" -"checksum proc-macro-nested 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0afe1bd463b9e9ed51d0e0f0b50b6b146aec855c56fd182bb242388710a9b6de" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -"checksum quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" -"checksum quickcheck_derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c21ac135ba4077d64a7f461ef1a131fa02a546ed85870053b70f739d254dd1e8" -"checksum quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7dfc1c4a1e048f5cc7d36a4c4118dfcf31d217c79f4b9a61bad65d68185752c" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_xoshiro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" -"checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -"checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -"checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -"checksum scoped-tls 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum send_wrapper 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" -"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" -"checksum serde_derive 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" -"checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" -"checksum sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -"checksum sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72377440080fd008550fe9b441e854e43318db116f90181eef92e9ae9aedab48" -"checksum simple_logger 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fea0c4611f32f4c2bac73754f22dca1f57e6c1945e0590dae4e5f2a077b92367" -"checksum sized-chunks 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smol 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "620cbb3c6e34da57d3a248cda0cd01cd5848164dc062e764e65d06fe3ea7aed5" -"checksum socket2 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" -"checksum stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09f8ed9974042b8c3672ff3030a69fcc03b74c47c3d1ecb7755e8a3626011e88" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" -"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" -"checksum subtle 2.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" -"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" -"checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" -"checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" -"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" -"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" -"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" -"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum waker-fn 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)" = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" -"checksum wasm-bindgen-backend 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)" = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101" -"checksum wasm-bindgen-futures 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6" -"checksum wasm-bindgen-macro 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)" = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3" -"checksum wasm-bindgen-macro-support 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)" = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92" -"checksum wasm-bindgen-shared 0.2.63 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd" -"checksum web-sys 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17" -"checksum wepoll-sys-stjepang 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum x25519-dalek 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" -"checksum zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" -"checksum zeroize_derive 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2" + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml index 779a191a9..4288a1c82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ members = [ "components/app", "components/test", "components/mutual_from", - "components/capnp_conv", - "components/capnp_conv/capnp_conv_derive", "components/signature", "components/route", "components/lockfile", diff --git a/README.md b/README.md index baaa69386..333d4c417 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,6 @@ the crate's `Cargo.toml`. ### Install dependencies - Install [Rust](https://www.rust-lang.org/tools/install). -- Install [capnproto](https://capnproto.org): - - On Ubuntu, run: `sudo apt install capnproto` - - On macOS, run: `brew install canpnp` ### Rust toolchain version diff --git a/ci/android_install.sh b/ci/android_install.sh index f3f20bb67..a2b7d0b1a 100755 --- a/ci/android_install.sh +++ b/ci/android_install.sh @@ -43,6 +43,3 @@ cd - # Make sure rust stable is installed rustup install stable - -# Install capnp: -ci/pre/capnp.sh diff --git a/ci/pre/capnp.sh b/ci/pre/capnp.sh deleted file mode 100755 index c0b085d54..000000000 --- a/ci/pre/capnp.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# See: https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ -set -eux -o pipefail - -CAPNP_INSTALL_PREFIX="${HOME}/install/capnp" - -CAPNP_VERSION="0.7.0" - -export CC="gcc-6" -export CXX="g++-6" -export CPPFLAGS="-std=c++14" -export CXXFLAGS="-std=c++14" - -# Build only if we don't have a cached installation: -if [ ! -d "$CAPNP_INSTALL_PREFIX/lib" ]; then - curl -L https://capnproto.org/capnproto-c++-${CAPNP_VERSION}.tar.gz | tar -zxf - - pushd capnproto-c++-${CAPNP_VERSION} - ./configure --prefix=${CAPNP_INSTALL_PREFIX} - make check -j2 - sudo make install - popd -fi - -sudo ln -s ${CAPNP_INSTALL_PREFIX}/bin/capnp /usr/local/bin/capnp diff --git a/components/app/src/lib.rs b/components/app/src/lib.rs index d03f3371e..158c33644 100644 --- a/components/app/src/lib.rs +++ b/components/app/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/app_client/src/lib.rs b/components/app_client/src/lib.rs index 0769f92f6..af409f409 100644 --- a/components/app_client/src/lib.rs +++ b/components/app_client/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/app_server/src/lib.rs b/components/app_server/src/lib.rs index 11f757760..4768309a6 100644 --- a/components/app_server/src/lib.rs +++ b/components/app_server/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/bin/src/bin/stindex.rs b/components/bin/src/bin/stindex.rs index 24405a0a0..1346c03d0 100644 --- a/components/bin/src/bin/stindex.rs +++ b/components/bin/src/bin/stindex.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/bin/src/bin/stmgr.rs b/components/bin/src/bin/stmgr.rs index bcf19d3fb..ee5dc70b4 100644 --- a/components/bin/src/bin/stmgr.rs +++ b/components/bin/src/bin/stmgr.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/bin/src/bin/stnode.rs b/components/bin/src/bin/stnode.rs index 50d830444..5ba772e60 100644 --- a/components/bin/src/bin/stnode.rs +++ b/components/bin/src/bin/stnode.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/bin/src/bin/strelay.rs b/components/bin/src/bin/strelay.rs index 2d2e5fd6a..86d63eec1 100644 --- a/components/bin/src/bin/strelay.rs +++ b/components/bin/src/bin/strelay.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/bin/src/lib.rs b/components/bin/src/lib.rs index 3bf207a93..27e9d4b18 100644 --- a/components/bin/src/lib.rs +++ b/components/bin/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/capnp_conv/Cargo.toml b/components/capnp_conv/Cargo.toml deleted file mode 100644 index 1376b0341..000000000 --- a/components/capnp_conv/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "offset-capnp-conv" -version = "0.1.0" -authors = ["real "] -license = "MIT OR Apache-2.0" -edition = "2018" - -[dependencies] - -log = "0.4" -pretty_env_logger = "0.2" - -offset-capnp-conv-derive = { path = "capnp_conv_derive", version = "0.1.0" } - -capnp = "0.10.0" -derive_more = "0.15.0" - -[build-dependencies] -capnpc = "0.10.0" - -[dev-dependencies] - -byteorder = {version = "1.3.2", features = ["i128"]} - diff --git a/components/capnp_conv/LICENSE-APACHE b/components/capnp_conv/LICENSE-APACHE deleted file mode 100644 index c753170bc..000000000 --- a/components/capnp_conv/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2019 real - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/components/capnp_conv/LICENSE-MIT b/components/capnp_conv/LICENSE-MIT deleted file mode 100644 index 683ce3d46..000000000 --- a/components/capnp_conv/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 real - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/components/capnp_conv/build.rs b/components/capnp_conv/build.rs deleted file mode 100644 index 1ef2c362e..000000000 --- a/components/capnp_conv/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - capnpc::CompilerCommand::new() - .src_prefix("tests/") - .file("tests/capnp/test.capnp") - .run() - .unwrap(); -} diff --git a/components/capnp_conv/capnp_conv_derive/Cargo.toml b/components/capnp_conv/capnp_conv_derive/Cargo.toml deleted file mode 100644 index 4716f688f..000000000 --- a/components/capnp_conv/capnp_conv_derive/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "offset-capnp-conv-derive" -version = "0.1.0" -authors = ["real "] -license = "MIT OR Apache-2.0" -edition = "2018" - -[lib] -proc-macro = true - -[dependencies] - -quote = "0.6.12" -syn = "0.15.38" -proc-macro2 = "0.4" -heck = "0.3.1" - diff --git a/components/capnp_conv/capnp_conv_derive/LICENSE-APACHE b/components/capnp_conv/capnp_conv_derive/LICENSE-APACHE deleted file mode 100644 index c753170bc..000000000 --- a/components/capnp_conv/capnp_conv_derive/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright (c) 2019 real - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/components/capnp_conv/capnp_conv_derive/LICENSE-MIT b/components/capnp_conv/capnp_conv_derive/LICENSE-MIT deleted file mode 100644 index 683ce3d46..000000000 --- a/components/capnp_conv/capnp_conv_derive/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 real - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/components/capnp_conv/capnp_conv_derive/src/derive_enum.rs b/components/capnp_conv/capnp_conv_derive/src/derive_enum.rs deleted file mode 100644 index bd4962054..000000000 --- a/components/capnp_conv/capnp_conv_derive/src/derive_enum.rs +++ /dev/null @@ -1,277 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::spanned::Spanned; -// use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident, Index}; -use syn::{DataEnum, Fields, Ident, Path, Variant}; - -use heck::SnakeCase; - -use crate::util::{ - capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_vec, is_data, is_primitive, - usize_to_u32_shim, CapnpWithAttribute, -}; - -// TODO: Deal with the case of multiple with attributes (Should report error) -/// Get the path from a with style field attribute. -/// Example: -/// ```text -/// #[capnp_conv(with = Wrapper)] -/// ``` -/// Will return the path `Wrapper` -fn get_with_attribute(variant: &syn::Variant) -> Option { - for attr in &variant.attrs { - if attr.path.is_ident("capnp_conv") { - let tts: proc_macro::TokenStream = attr.tts.clone().into(); - let capnp_with_attr = syn::parse::(tts).unwrap(); - return Some(capnp_with_attr.path); - } - } - None -} - -fn gen_type_write(variant: &Variant, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { - let opt_with_path = get_with_attribute(variant); - // let variant_ident = &variant.ident; - let variant_name = &variant.ident; - let variant_snake_name = variant_name.to_string().to_snake_case(); - - match &variant.fields { - Fields::Unnamed(fields_unnamed) => { - let unnamed = &fields_unnamed.unnamed; - if unnamed.len() != 1 { - unimplemented!("gen_type_write: Amount of unnamed fields is not 1!"); - } - - let pair = unnamed.last().unwrap(); - let last_ident = match pair { - syn::punctuated::Pair::End(last_ident) => last_ident, - _ => unreachable!(), - }; - - let path = match opt_with_path { - Some(with_path) => with_path, - None => match &last_ident.ty { - syn::Type::Path(type_path) => type_path.path.clone(), - _ => { - panic!("{:?}", opt_with_path); - } - }, - }; - /* - let path = opt_with_path.clone().unwrap_or(match &last_ident.ty { - syn::Type::Path(type_path) => type_path.path.clone(), - _ => { - panic!("{:?}", opt_with_path); - // unimplemented!("gen_type_write: last ident is not a path!"), - } - }); - */ - - let mut path = path; - assign_defaults(&mut path); - - if is_primitive(&path) { - let set_method = - syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); - return quote! { - #variant_name(x) => writer.#set_method(<#path>::from(x.clone())), - }; - } - - if is_data(&path) { - let set_method = - syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); - return quote! { - #variant_name(x) => writer.#set_method(&<#path>::from(x.clone())), - }; - } - - if path.is_ident("String") { - let set_method = - syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); - return quote! { - #variant_name(x) => writer.#set_method(x), - }; - } - - // The case of list: - if let Some(inner_path) = get_vec(&path) { - let init_method = - syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); - let list_write_iter = gen_list_write_iter(&inner_path); - - // In the cases of more complicated types, list_builder needs to be mutable. - let let_list_builder = - if is_primitive(&path) || path.is_ident("String") || is_data(&path) { - quote! { let list_builder } - } else { - quote! { let mut list_builder } - }; - - let usize_to_u32 = usize_to_u32_shim(); - return quote! { - #variant_name(vec) => { - #usize_to_u32 - #let_list_builder = writer - .reborrow() - .#init_method(usize_to_u32(vec.len()).unwrap()); - - for (index, item) in vec.iter().enumerate() { - #list_write_iter - } - }, - }; - } - - let init_method = - syn::Ident::new(&format!("init_{}", &variant_snake_name), variant.span()); - quote! { - #variant_name(x) => <#path>::from(x.clone()).write_capnp(&mut writer.reborrow().#init_method()), - } - } - - Fields::Unit => { - let set_method = - syn::Ident::new(&format!("set_{}", &variant_snake_name), variant.span()); - quote! { - #variant_name => writer.#set_method(()), - } - } - // Rust enum variants don't have named fields (?) - Fields::Named(_) => unreachable!(), - } -} - -pub fn gen_write_capnp_enum( - data_enum: &DataEnum, - rust_enum: &Ident, - capnp_struct: &Path, - assign_defaults: impl Fn(&mut syn::Path), -) -> TokenStream { - let recurse = data_enum.variants.iter().map(|variant| { - let type_write = gen_type_write(&variant, &assign_defaults); - quote! { - #rust_enum::#type_write - } - }); - - quote! { - impl<'a> WriteCapnp<'a> for #rust_enum { - type WriterType = #capnp_struct::Builder<'a>; - fn write_capnp(&self, writer: &mut Self::WriterType) { - match &self { - #(#recurse)* - }; - } - } - } -} - -fn gen_type_read( - variant: &Variant, - rust_enum: &Ident, - assign_defaults: impl Fn(&mut syn::Path), -) -> TokenStream { - let opt_with_path = get_with_attribute(variant); - let variant_name = &variant.ident; - // let variant_snake_name = variant_name.to_string().to_snake_case(); - - match &variant.fields { - Fields::Unnamed(fields_unnamed) => { - let unnamed = &fields_unnamed.unnamed; - if unnamed.len() != 1 { - unimplemented!("gen_type_read: Amount of unnamed fields is not 1!"); - } - - let pair = unnamed.last().unwrap(); - let last_ident = match pair { - syn::punctuated::Pair::End(last_ident) => last_ident, - _ => unreachable!(), - }; - - let mut path = match opt_with_path { - Some(with_path) => with_path, - None => match &last_ident.ty { - syn::Type::Path(type_path) => type_path.path.clone(), - _ => { - panic!("{:?}", opt_with_path); - } - }, - }; - - assign_defaults(&mut path); - - if is_primitive(&path) { - return quote! { - #variant_name(x) => #rust_enum::#variant_name(x.into()), - }; - } - - if is_data(&path) || path.is_ident("String") { - return quote! { - #variant_name(x) => #rust_enum::#variant_name(x?.into()), - }; - } - - if let Some(inner_path) = get_vec(&path) { - // The case of a list: - let list_read_iter = gen_list_read_iter(&inner_path); - return quote! { - #variant_name(list_reader) => { - let mut res_vec = Vec::new(); - for item_reader in list_reader? { - // res_vec.push_back(read_named_relay_address(&named_relay_address)?); - #list_read_iter - } - #rust_enum::#variant_name(res_vec) - } - }; - } - - let capnp_result = capnp_result_shim(); - - quote! { - #variant_name(variant_reader) => { - #capnp_result - - let variant_reader = CapnpResult::from(variant_reader).into_result()?; - #rust_enum::#variant_name(<#path>::read_capnp(&variant_reader)?.into()) - }, - } - } - - Fields::Unit => { - quote! { - #variant_name(()) => #rust_enum::#variant_name, - } - } - // Rust enum variants don't have named fields (?) - Fields::Named(_) => unreachable!(), - } -} - -pub fn gen_read_capnp_enum( - data_enum: &DataEnum, - rust_enum: &Ident, - capnp_struct: &Path, - assign_defaults: impl Fn(&mut syn::Path), -) -> TokenStream { - let recurse = data_enum.variants.iter().map(|variant| { - let type_read = gen_type_read(&variant, rust_enum, &assign_defaults); - quote! { - #capnp_struct::#type_read - } - }); - - quote! { - impl<'a> ReadCapnp<'a> for #rust_enum { - type ReaderType = #capnp_struct::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - Ok(match reader.which()? { - #(#recurse)* - }) - } - } - } -} diff --git a/components/capnp_conv/capnp_conv_derive/src/derive_struct.rs b/components/capnp_conv/capnp_conv_derive/src/derive_struct.rs deleted file mode 100644 index bde1bc0c3..000000000 --- a/components/capnp_conv/capnp_conv_derive/src/derive_struct.rs +++ /dev/null @@ -1,205 +0,0 @@ -use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; - -use syn::spanned::Spanned; -use syn::{FieldsNamed, Ident, Path}; - -use crate::util::{ - capnp_result_shim, gen_list_read_iter, gen_list_write_iter, get_vec, is_data, is_primitive, - usize_to_u32_shim, CapnpWithAttribute, -}; - -// TODO: Deal with the case of multiple with attributes (Should report error) -/// Get the path from a with style field attribute. -/// Example: -/// ```text -/// #[capnp_conv(with = Wrapper)] -/// ``` -/// Will return the path `Wrapper` -fn get_with_attribute(field: &syn::Field) -> Option { - for attr in &field.attrs { - if attr.path.is_ident("capnp_conv") { - let tts: proc_macro::TokenStream = attr.tts.clone().into(); - let capnp_with_attr = syn::parse::(tts).unwrap(); - return Some(capnp_with_attr.path); - } - } - None -} - -fn gen_type_write(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { - let opt_with_path = get_with_attribute(field); - match &field.ty { - syn::Type::Path(type_path) => { - if type_path.qself.is_some() { - // Self qualifier? - unimplemented!("self qualifier"); - } - - let mut path = type_path.path.clone(); - assign_defaults(&mut path); - let path = opt_with_path.unwrap_or(path); - - let name = &field.ident.as_ref().unwrap(); - - if is_primitive(&path) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - return quote_spanned! {field.span() => - writer.reborrow().#set_method(<#path>::from(self.#name)); - }; - } - - if path.is_ident("String") || is_data(&path) { - let set_method = syn::Ident::new(&format!("set_{}", &name), name.span()); - return quote_spanned! {field.span() => - writer.reborrow().#set_method(&<#path>::from(self.#name.clone())); - }; - } - - if let Some(inner_path) = get_vec(&path) { - let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); - let list_write_iter = gen_list_write_iter(&inner_path); - - // In the cases of more complicated types, list_builder needs to be mutable. - let let_list_builder = - if is_primitive(&path) || path.is_ident("String") || is_data(&path) { - quote! { let list_builder } - } else { - quote! { let mut list_builder } - }; - - let usize_to_u32 = usize_to_u32_shim(); - - return quote_spanned! {field.span() => - { - #usize_to_u32 - - #let_list_builder = { - writer - .reborrow() - .#init_method(usize_to_u32(self.#name.len()).unwrap()) - }; - - for (index, item) in self.#name.iter().enumerate() { - #list_write_iter - } - } - }; - } - - // Generic type: - let init_method = syn::Ident::new(&format!("init_{}", &name), name.span()); - quote_spanned! {field.span() => - <#path>::from(self.#name.clone()).write_capnp(&mut writer.reborrow().#init_method()); - } - } - _ => unimplemented!(), - } -} - -fn gen_type_read(field: &syn::Field, assign_defaults: impl Fn(&mut syn::Path)) -> TokenStream { - let opt_with_path = get_with_attribute(field); - - match &field.ty { - syn::Type::Path(type_path) => { - if type_path.qself.is_some() { - // Self qualifier? - unimplemented!("self qualifier"); - } - - let mut path = type_path.path.clone(); - assign_defaults(&mut path); - - let path = opt_with_path.unwrap_or(path); - - let name = &field.ident.as_ref().unwrap(); - - if is_primitive(&path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - return quote_spanned! {field.span() => - #name: reader.#get_method().into() - }; - } - - if path.is_ident("String") || is_data(&path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - return quote_spanned! {field.span() => - #name: reader.#get_method()?.into() - }; - } - - if let Some(inner_path) = get_vec(&path) { - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - let list_read_iter = gen_list_read_iter(&inner_path); - return quote_spanned! {field.span() => - #name: { - let mut res_vec = Vec::new(); - for item_reader in reader.#get_method()? { - // res_vec.push_back(read_named_relay_address(&named_relay_address)?); - #list_read_iter - } - res_vec.into() - } - }; - } - - // Generic type: - let get_method = syn::Ident::new(&format!("get_{}", &name), name.span()); - let capnp_result = capnp_result_shim(); - quote_spanned! {field.span() => - #name: { - #capnp_result - let inner_reader = CapnpResult::from(reader.#get_method()).into_result()?; - <#path>::read_capnp(&inner_reader)?.into() - } - } - } - _ => unimplemented!(), - } -} - -pub fn gen_write_capnp_named_struct( - fields_named: &FieldsNamed, - rust_struct: &Ident, - capnp_struct: &Path, - assign_defaults: impl Fn(&mut syn::Path), -) -> TokenStream { - let recurse = fields_named - .named - .iter() - .map(|field| gen_type_write(&field, &assign_defaults)); - - quote! { - impl<'a> WriteCapnp<'a> for #rust_struct { - type WriterType = #capnp_struct::Builder<'a>; - - fn write_capnp(&self, writer: &mut Self::WriterType) { - #(#recurse)* - } - } - } -} - -pub fn gen_read_capnp_named_struct( - fields_named: &FieldsNamed, - rust_struct: &Ident, - capnp_struct: &Path, - assign_defaults: impl Fn(&mut syn::Path), -) -> TokenStream { - let recurse = fields_named - .named - .iter() - .map(|field| gen_type_read(field, &assign_defaults)); - - quote! { - impl<'a> ReadCapnp<'a> for #rust_struct { - type ReaderType = #capnp_struct::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - Ok(#rust_struct { - #(#recurse,)* - }) - } - } - } -} diff --git a/components/capnp_conv/capnp_conv_derive/src/lib.rs b/components/capnp_conv/capnp_conv_derive/src/lib.rs deleted file mode 100644 index 713fdfc28..000000000 --- a/components/capnp_conv/capnp_conv_derive/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -#![crate_type = "lib"] -#![recursion_limit = "128"] -#![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] -#![allow( - clippy::too_many_arguments, - clippy::implicit_hasher, - clippy::module_inception, - clippy::new_without_default -)] -#![allow(unreachable_code)] - -extern crate proc_macro; - -mod derive_enum; -mod derive_struct; -mod util; - -use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput, Fields, Path}; - -use self::derive_enum::{gen_read_capnp_enum, gen_write_capnp_enum}; -use self::derive_struct::{gen_read_capnp_named_struct, gen_write_capnp_named_struct}; -use self::util::{assign_defaults_path, extract_defaults, remove_with_attributes}; - -/// Generate code for conversion between Rust and capnp structs. -#[proc_macro_attribute] -pub fn capnp_conv( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - // See: https://github.com/dtolnay/syn/issues/86 - // for information about arguments. - - // Name of capnp struct: - let capnp_struct = parse_macro_input!(args as Path); - let input = parse_macro_input!(input as DeriveInput); - - let defaults = extract_defaults(&input.generics); - let assign_defaults = |path: &mut syn::Path| assign_defaults_path(path, &defaults); - - // Name of local struct: - let rust_struct = &input.ident; - - let conversion = match input.data { - Data::Struct(ref data) => match data.fields { - Fields::Named(ref fields_named) => { - // Example: - // struct Point { - // x: u32, - // y: u32, - // } - - let write_capnp = gen_write_capnp_named_struct( - fields_named, - rust_struct, - &capnp_struct, - &assign_defaults, - ); - let read_capnp = gen_read_capnp_named_struct( - fields_named, - rust_struct, - &capnp_struct, - &assign_defaults, - ); - - quote! { - #[allow(clippy::all)] - #write_capnp - #[allow(clippy::all)] - #read_capnp - } - } - Fields::Unnamed(_) | Fields::Unit => unimplemented!(), - }, - Data::Enum(ref data_enum) => { - // Example: - // enum MyEnum { - // Type1(u32), - // Type2, - // Type3(MyStruct), - // } - let write_capnp = - gen_write_capnp_enum(data_enum, rust_struct, &capnp_struct, &assign_defaults); - let read_capnp = - gen_read_capnp_enum(data_enum, rust_struct, &capnp_struct, &assign_defaults); - - quote! { - #[allow(clippy::all)] - #write_capnp - #[allow(clippy::all)] - #read_capnp - } - } - Data::Union(_) => unimplemented!(), - }; - - // Remove all of our `#[capnp_conv(with = ... )]` attributes from the input: - let mut input = input; - remove_with_attributes(&mut input); - - let expanded = quote! { - // Original structure - #input - // Generated mutual From conversion code: - #conversion - }; - - proc_macro::TokenStream::from(expanded) -} diff --git a/components/capnp_conv/capnp_conv_derive/src/util.rs b/components/capnp_conv/capnp_conv_derive/src/util.rs deleted file mode 100644 index 7f94cb7a7..000000000 --- a/components/capnp_conv/capnp_conv_derive/src/util.rs +++ /dev/null @@ -1,333 +0,0 @@ -use std::collections::HashMap; - -use proc_macro2::TokenStream; -use quote::quote; - -/// A shim for converting usize to u32 -pub fn usize_to_u32_shim() -> TokenStream { - quote! { - pub fn usize_to_u32(num: usize) -> Option { - if num > 0xffffffff as usize { - None - } else { - Some(num as u32) - } - } - } -} - -/// Is a primitive type? -pub fn is_primitive(path: &syn::Path) -> bool { - path.is_ident("u8") - || path.is_ident("u16") - || path.is_ident("u32") - || path.is_ident("u64") - || path.is_ident("i8") - || path.is_ident("i16") - || path.is_ident("i32") - || path.is_ident("i64") - || path.is_ident("f32") - || path.is_ident("f64") - || path.is_ident("bool") -} - -/// Check if the path represents a Vec -pub fn is_data(path: &syn::Path) -> bool { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return false; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return false, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return false, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return false, - }; - - if arg_ty_path.qself.is_some() { - return false; - } - - if !arg_ty_path.path.is_ident("u8") { - return false; - } - - true -} - -/// Check if the path represents a Vec, where SomeStruct != u8 -pub fn get_vec(path: &syn::Path) -> Option { - let last_segment = match path.segments.last().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - if &last_segment.ident.to_string() != "Vec" { - return None; - } - let angle = match &last_segment.arguments { - syn::PathArguments::AngleBracketed(angle) => { - if angle.args.len() > 1 { - unreachable!("Too many arguments for Vec!"); - } - angle - } - _ => unreachable!("Vec with arguments that are not angle bracketed!"), - }; - let last_arg = match angle.args.last().unwrap() { - syn::punctuated::Pair::End(last_arg) => last_arg, - _ => return None, - }; - - let arg_ty = match last_arg { - syn::GenericArgument::Type(arg_ty) => arg_ty, - _ => return None, - }; - - let arg_ty_path = match arg_ty { - syn::Type::Path(arg_ty_path) => arg_ty_path, - _ => return None, - }; - - if arg_ty_path.qself.is_some() { - return None; - } - - // Make sure that we don't deal with Vec: - if arg_ty_path.path.is_ident("u8") { - return None; - } - - Some(arg_ty_path.path.clone()) -} - -pub fn gen_list_write_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - list_builder - .reborrow() - .set(usize_to_u32(index).unwrap(), item.clone().into()); - } - } else { - // Not a primitive list: - quote! { - let mut item_builder = list_builder - .reborrow() - .get(usize_to_u32(index).unwrap()); - - item.clone().write_capnp(&mut item_builder); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} - -pub fn gen_list_read_iter(path: &syn::Path) -> TokenStream { - if is_primitive(path) || path.is_ident("String") || is_data(path) { - // A primitive list: - quote! { - res_vec.push(item_reader.into()); - } - } else { - // Not a primitive list: - quote! { - res_vec.push(<#path>::read_capnp(&item_reader)?); - } - } - // TODO: It seems like we do not support List(List(...)) at the moment. - // How to support it? -} - -/// A shim allowing to merge cases where either -/// Result> or a T is returned. -pub fn capnp_result_shim() -> TokenStream { - quote! { - pub enum CapnpResult { - Ok(T), - Err(CapnpConvError), - } - - impl CapnpResult { - pub fn into_result(self) -> Result { - match self { - CapnpResult::Ok(t) => Ok(t), - CapnpResult::Err(e) => Err(e), - } - } - } - - impl From for CapnpResult { - fn from(input: T) -> Self { - CapnpResult::Ok(input) - } - } - - impl From> for CapnpResult - where - E: Into, - { - fn from(input: Result) -> Self { - match input { - Ok(t) => CapnpResult::Ok(t), - Err(e) => CapnpResult::Err(e.into()), - } - } - } - } -} - -/// Obtain a map of default values from generics. -/// Example: -/// -/// ```text -/// struct MyStruct { ... } -/// ``` -/// -/// We expect to get a map, mapping A -> u32, B -> u64. -/// -pub fn extract_defaults(generics: &syn::Generics) -> HashMap { - let mut defaults = HashMap::new(); - for param in &generics.params { - let type_param = match *param { - syn::GenericParam::Type(ref type_param) => type_param, - _ => continue, - }; - - if type_param.eq_token.is_none() { - continue; - }; - - let default_type = match &type_param.default { - Some(default_type) => default_type, - None => continue, - }; - - let default_type_path = match default_type { - syn::Type::Path(default_type_path) => default_type_path, - _ => unimplemented!("Only paths default params are supported"), - }; - - if default_type_path.qself.is_some() { - unimplemented!("qself is not implemented!"); - } - - defaults.insert(type_param.ident.clone(), default_type_path.path.clone()); - } - defaults -} - -/// For every generic along a path, assign a default value if possible -pub fn assign_defaults_path(path: &mut syn::Path, defaults: &HashMap) { - // Deal with the case of a single Ident: `T` - - if path.segments.len() == 1 { - let last_segment = match path.segments.last_mut().unwrap() { - syn::punctuated::Pair::End(last_segment) => last_segment, - _ => unreachable!(), - }; - - if let syn::PathArguments::None = last_segment.arguments { - if let Some(default_path) = defaults.get(&last_segment.ident) { - let _ = std::mem::replace(path, default_path.clone()); - return; - } - } - } - - // Deal with the more general case of a Path with various arguments - // that should be assigned their default value - - for segment in path.segments.iter_mut() { - let args = match &mut segment.arguments { - syn::PathArguments::None => continue, - syn::PathArguments::AngleBracketed(angle_bracketed) => &mut angle_bracketed.args, - _ => unimplemented!("Only angle bracketed arguments are supported!"), - }; - - for generic_arg in args.iter_mut() { - let ty = match generic_arg { - syn::GenericArgument::Type(ty) => ty, - _ => unimplemented!(), - }; - - let type_path = match ty { - syn::Type::Path(type_path) => type_path, - _ => unimplemented!(), - }; - - if type_path.qself.is_some() { - unimplemented!(); - } - - // Recursively replace default arguments: - assign_defaults_path(&mut type_path.path, defaults); - } - } -} - -/// Remove all of our `#[capnp_conv(with = ...)]` attributes -pub fn remove_with_attributes(input: &mut syn::DeriveInput) { - match input.data { - syn::Data::Struct(ref mut data) => match data.fields { - syn::Fields::Named(ref mut fields_named) => { - for field in fields_named.named.iter_mut() { - // Remove all the attributes that look like: `capnp_conv(...)` - field.attrs.retain(|attr| !attr.path.is_ident("capnp_conv")); - } - } - syn::Fields::Unnamed(_) | syn::Fields::Unit => unimplemented!(), - }, - syn::Data::Enum(ref mut data_enum) => { - for variant in data_enum.variants.iter_mut() { - // Remove all the attributes that look like: `capnp_conv(...)` - variant - .attrs - .retain(|attr| !attr.path.is_ident("capnp_conv")); - } - } - - syn::Data::Union(_) => unimplemented!(), - }; -} - -#[derive(Debug)] -pub struct CapnpWithAttribute { - #[allow(dead_code)] - pub paren_token: syn::token::Paren, - pub with_ident: syn::Ident, - pub eq_token: syn::Token![=], - pub path: syn::Path, -} - -impl syn::parse::Parse for CapnpWithAttribute { - fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { - let content; - let paren_token = syn::parenthesized!(content in input); - Ok(Self { - paren_token, - with_ident: content.parse()?, - eq_token: content.parse()?, - path: content.parse()?, - }) - } -} diff --git a/components/capnp_conv/src/lib.rs b/components/capnp_conv/src/lib.rs deleted file mode 100644 index 61e8a952e..000000000 --- a/components/capnp_conv/src/lib.rs +++ /dev/null @@ -1,89 +0,0 @@ -#![crate_type = "lib"] -#![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] -#![allow( - clippy::too_many_arguments, - clippy::implicit_hasher, - clippy::module_inception, - clippy::new_without_default -)] - -// Workaround for issue: https://github.com/rust-lang/rust/issues/64450 -extern crate offset_capnp_conv_derive as capnp_conv_derive; - -use std::io; - -use derive_more::From; - -pub use capnp_conv_derive::capnp_conv; - -#[derive(Debug, From)] -pub enum CapnpConvError { - CapnpError(capnp::Error), - NotInSchema(capnp::NotInSchema), - IoError(io::Error), -} - -/// Convert Rust struct to Capnp. -pub trait WriteCapnp<'a> { - /// The corresponding Capnp writer type. - // type WriterType: capnp::traits::FromPointerBuilder<'a>; - type WriterType: capnp::traits::FromPointerBuilder<'a>; - - /// Converts a Rust struct to corresponding Capnp struct. This should not fail. - fn write_capnp(&self, writer: &mut Self::WriterType); -} - -/// Convert Capnp struct to Rust. -pub trait ReadCapnp<'a>: Sized { - /// The corresponding Capnp reader type. - type ReaderType: capnp::traits::FromPointerReader<'a>; - - /// Converts a Capnp struct to corresponding Rust struct. - fn read_capnp(reader: &Self::ReaderType) -> Result; -} - -pub trait ToCapnpBytes { - /// Serialize a Rust struct into bytes using Capnp - fn to_capnp_bytes(&self) -> Vec; -} - -pub trait FromCapnpBytes: Sized { - /// Deserialize a Rust struct from bytes using Capnp - fn from_capnp_bytes(bytes: &[u8]) -> Result; -} - -impl ToCapnpBytes for T -where - T: for<'a> WriteCapnp<'a>, -{ - fn to_capnp_bytes(&self) -> Vec { - let mut builder = capnp::message::Builder::new_default(); - - // A trick to avoid borrow checker issues: - { - let mut struct_builder = builder.init_root::(); - self.write_capnp(&mut struct_builder); - } - - let mut data = Vec::new(); - // Should never really fail: - capnp::serialize_packed::write_message(&mut data, &builder).unwrap(); - data - } -} - -impl FromCapnpBytes for T -where - T: for<'a> ReadCapnp<'a>, -{ - fn from_capnp_bytes(bytes: &[u8]) -> Result { - let mut cursor = io::Cursor::new(&bytes); - let reader = capnp::serialize_packed::read_message( - &mut cursor, - capnp::message::ReaderOptions::new(), - )?; - let struct_reader = reader.get_root::()?; - Ok(Self::read_capnp(&struct_reader)?) - } -} diff --git a/components/capnp_conv/tests/capnp/test.capnp b/components/capnp_conv/tests/capnp/test.capnp deleted file mode 100644 index 61ca67a6b..000000000 --- a/components/capnp_conv/tests/capnp/test.capnp +++ /dev/null @@ -1,109 +0,0 @@ -@0xc90daeac68e62b2a; - -struct TestStructInner { - innerU8 @0: UInt8; -} - -struct TestUnion { - union { - variantOne @0: UInt64; - variantTwo @1: TestStructInner; - variantThree @2: Void; - variantFour @3: Text; - } -} - -struct ListUnion { - union { - empty @0: Void; - withList @1: List(TestStructInner); - withData @2: Data; - testUnion @3: TestUnion; - inlineInnerUnion: union { - ab @4: UInt32; - cd @5: UInt64; - } - } -} - -struct TestStruct { - myBool @0: Bool; - myInt8 @1: Int8; - myInt16 @2: Int16; - myInt32 @3: Int32; - myInt64 @4: Int64; - myUint8 @5: UInt8; - myUint16 @6: UInt16; - myUint32 @7: UInt32; - myUint64 @8: UInt64; - # my_float32: f32, - # my_float64: f64, - myText @9: Text; - myData @10: Data; - structInner @11: TestStructInner; - myPrimitiveList @12: List(UInt16); - myList @13: List(TestStructInner); - inlineUnion: union { - firstVariant @14: UInt64; - secondVariant @15: TestStructInner; - thirdVariant @16: Void; - } - externalUnion @17: TestUnion; - listUnion @18: ListUnion; -} - -struct FloatStruct { - myFloat32 @0: Float32; - myFloat64 @1: Float64; -} - -struct GenericStruct { - a @0: UInt32; - b @1: UInt64; - c @2: UInt8; - d @3: Data; - e @4: List(TestStructInner); - f @5: TestStructInner; -} - -struct GenericEnum { - union { - varA @0: UInt32; - varB @1: TestStructInner; - varC @2: UInt64; - varD @3: Data; - } -} - -struct InnerGeneric { - a @0: UInt32; -} - -struct ListGeneric { - list @0: List(InnerGeneric); -} - -# A custom made 128 bit data structure. -struct Buffer128 { - x0 @0: UInt64; - x1 @1: UInt64; -} - -# Unsigned 128 bit integer -struct CustomUInt128 { - inner @0: Buffer128; -} - - -struct TestWithStruct { - a @0: CustomUInt128; - b @1: UInt64; -} - -struct TestWithEnum { - union { - varA @0: CustomUInt128; - varB @1: UInt64; - varC @2: Void; - } -} diff --git a/components/capnp_conv/tests/derive.rs b/components/capnp_conv/tests/derive.rs deleted file mode 100644 index e68a56e61..000000000 --- a/components/capnp_conv/tests/derive.rs +++ /dev/null @@ -1,221 +0,0 @@ -#![deny(warnings)] - -use offset_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, -}; - -#[allow(unused)] -mod test_capnp { - include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); -} - -#[capnp_conv(test_capnp::test_struct_inner)] -#[derive(Debug, Clone, PartialEq)] -struct TestStructInner { - inner_u8: u8, -} - -#[capnp_conv(test_capnp::test_struct::inline_union)] -#[derive(Debug, Clone, PartialEq)] -enum InlineUnion { - FirstVariant(u64), - SecondVariant(TestStructInner), - ThirdVariant, -} - -#[capnp_conv(test_capnp::test_union)] -#[derive(Debug, Clone, PartialEq)] -enum TestUnion { - VariantOne(u64), - VariantTwo(TestStructInner), - VariantThree, - VariantFour(String), -} - -#[capnp_conv(test_capnp::list_union::inline_inner_union)] -#[derive(Debug, Clone, PartialEq)] -enum InlineInnerUnion { - Ab(u32), - Cd(u64), -} - -#[capnp_conv(test_capnp::list_union)] -#[derive(Debug, Clone, PartialEq)] -enum ListUnion { - Empty, - WithList(Vec), - WithData(Vec), - TestUnion(TestUnion), - InlineInnerUnion(InlineInnerUnion), -} - -#[capnp_conv(test_capnp::test_struct)] -#[derive(Debug, Clone, PartialEq)] -struct TestStruct { - my_bool: bool, - my_int8: i8, - my_int16: i16, - my_int32: i32, - my_int64: i64, - my_uint8: u8, - my_uint16: u16, - my_uint32: u32, - my_uint64: u64, - my_text: String, - my_data: Vec, - struct_inner: TestStructInner, - my_primitive_list: Vec, - my_list: Vec, - inline_union: InlineUnion, - external_union: TestUnion, - list_union: ListUnion, -} - -#[test] -fn capnp_serialize_basic_struct() { - let test_struct = TestStruct { - my_bool: true, - my_int8: -1i8, - my_int16: 1i16, - my_int32: -1i32, - my_int64: 1i64, - my_uint8: 1u8, - my_uint16: 2u16, - my_uint32: 3u32, - my_uint64: 4u64, - my_text: "my_text".to_owned(), - my_data: vec![1, 2, 3, 4, 5u8], - struct_inner: TestStructInner { inner_u8: 1u8 }, - my_primitive_list: vec![10, 11, 12, 13, 14u16], - my_list: vec![ - TestStructInner { inner_u8: 2u8 }, - TestStructInner { inner_u8: 3u8 }, - TestStructInner { inner_u8: 4u8 }, - ], - inline_union: InlineUnion::SecondVariant(TestStructInner { inner_u8: 5u8 }), - external_union: TestUnion::VariantOne(6u64), - list_union: ListUnion::WithList(vec![ - TestStructInner { inner_u8: 10u8 }, - TestStructInner { inner_u8: 11u8 }, - ]), - }; - - let data = test_struct.to_capnp_bytes(); - let test_struct2 = TestStruct::from_capnp_bytes(&data).unwrap(); - - assert_eq!(test_struct, test_struct2); -} - -#[capnp_conv(test_capnp::float_struct)] -#[derive(Debug, Clone)] -struct FloatStruct { - my_float32: f32, - my_float64: f64, -} - -/// We test floats separately, because in Rust floats to not implement PartialEq -#[test] -fn capnp_serialize_floats() { - let float_struct = FloatStruct { - my_float32: -0.5f32, - my_float64: 0.5f64, - }; - - let data = float_struct.to_capnp_bytes(); - let float_struct2 = FloatStruct::from_capnp_bytes(&data).unwrap(); - - // Sloppily check that the floats are close enough (We can't compare them directly, as they - // don't implement PartialEq) - assert_eq!( - (float_struct.my_float32 * 10000.0).trunc(), - (float_struct2.my_float32 * 10000.0).trunc() - ); - - assert_eq!( - (float_struct.my_float64 * 10000.0).trunc(), - (float_struct2.my_float64 * 10000.0).trunc() - ); -} - -#[capnp_conv(test_capnp::generic_struct)] -#[derive(Debug, Clone, PartialEq)] -struct GenericStruct { - a: A, - pub b: B, - c: u8, - pub d: Vec, - e: Vec, - f: E, -} - -#[test] -fn capnp_serialize_generic_struct() { - let generic_struct = GenericStruct { - a: 1u32, - b: 2u64, - c: 3u8, - d: vec![1, 2, 3, 4u8], - e: vec![ - TestStructInner { inner_u8: 2u8 }, - TestStructInner { inner_u8: 3u8 }, - TestStructInner { inner_u8: 4u8 }, - ], - f: TestStructInner { inner_u8: 5u8 }, - }; - - let data = generic_struct.to_capnp_bytes(); - let generic_struct2 = GenericStruct::from_capnp_bytes(&data).unwrap(); - - assert_eq!(generic_struct, generic_struct2); -} - -#[capnp_conv(test_capnp::generic_enum)] -#[derive(Debug, Clone, PartialEq)] -enum GenericEnum> { - VarA(A), - VarB(B), - VarC(u64), - VarD(V), -} - -#[test] -fn capnp_serialize_generic_enum() { - for generic_enum in &[ - GenericEnum::VarA(1u32), - GenericEnum::VarB(TestStructInner { inner_u8: 2u8 }), - GenericEnum::VarC(3u64), - GenericEnum::VarD(vec![1, 2, 3, 4u8]), - ] { - let data = generic_enum.to_capnp_bytes(); - let generic_enum2 = GenericEnum::from_capnp_bytes(&data).unwrap(); - assert_eq!(generic_enum.clone(), generic_enum2); - } -} - -#[capnp_conv(test_capnp::inner_generic)] -#[derive(Debug, Clone, PartialEq)] -struct InnerGeneric { - a: A, -} - -#[capnp_conv(test_capnp::list_generic)] -#[derive(Debug, Clone, PartialEq)] -struct ListGeneric { - list: Vec>, -} - -#[test] -fn capnp_serialize_generic_list() { - let list_generic = ListGeneric { - list: vec![ - InnerGeneric { a: 1u32 }, - InnerGeneric { a: 2u32 }, - InnerGeneric { a: 3u32 }, - ], - }; - - let data = list_generic.to_capnp_bytes(); - let list_generic2 = ListGeneric::from_capnp_bytes(&data).unwrap(); - - assert_eq!(list_generic, list_generic2); -} diff --git a/components/capnp_conv/tests/with.rs b/components/capnp_conv/tests/with.rs deleted file mode 100644 index 733711dfd..000000000 --- a/components/capnp_conv/tests/with.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![deny(warnings)] - -use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; -#[allow(unused)] -use offset_capnp_conv::{ - capnp_conv, CapnpConvError, FromCapnpBytes, ReadCapnp, ToCapnpBytes, WriteCapnp, -}; - -#[allow(unused)] -mod test_capnp { - include!(concat!(env!("OUT_DIR"), "/capnp/test_capnp.rs")); -} - -#[derive(derive_more::Constructor, Debug, Clone, Copy, PartialEq, Eq)] -pub struct Wrapper(T); - -/* -impl std::ops::Deref for Wrapper { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -}*/ - -impl From for Wrapper { - fn from(t: T) -> Self { - Wrapper(t) - } -} - -impl Into for Wrapper { - fn into(self) -> u128 { - self.0 - } -} - -impl<'a> WriteCapnp<'a> for Wrapper { - type WriterType = crate::test_capnp::custom_u_int128::Builder<'a>; - - fn write_capnp(&self, writer: &mut Self::WriterType) { - let mut inner = writer.reborrow().get_inner().unwrap(); - - let mut data_bytes = Vec::new(); - - data_bytes - .write_u128::(self.clone().into()) - .unwrap(); - let mut cursor = std::io::Cursor::new(AsRef::<[u8]>::as_ref(&data_bytes)); - - inner.set_x0(cursor.read_u64::().unwrap()); - inner.set_x1(cursor.read_u64::().unwrap()); - } -} - -impl<'a> ReadCapnp<'a> for Wrapper { - type ReaderType = crate::test_capnp::custom_u_int128::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - let inner = reader.get_inner()?; - let mut vec = Vec::new(); - vec.write_u64::(inner.get_x0())?; - vec.write_u64::(inner.get_x1())?; - Ok(Wrapper::new(BigEndian::read_u128(&vec[..]))) - } -} - -#[capnp_conv(test_capnp::test_with_struct)] -#[derive(Debug, Clone, PartialEq)] -struct TestWithStruct { - #[capnp_conv(with = Wrapper)] - a: u128, - b: u64, -} - -#[test] -fn capnp_serialize_with_struct() { - let test_with_struct = TestWithStruct { a: 1u128, b: 2u64 }; - - let data = test_with_struct.to_capnp_bytes(); - let test_with_struct2 = TestWithStruct::from_capnp_bytes(&data).unwrap(); - - assert_eq!(test_with_struct, test_with_struct2); -} - -#[capnp_conv(test_capnp::test_with_enum)] -#[derive(Debug, Clone, PartialEq)] -enum TestWithEnum { - #[capnp_conv(with = Wrapper)] - VarA(u128), - VarB(u64), - VarC, -} - -#[test] -fn capnp_serialize_with_enum() { - let test_with_enum = TestWithEnum::VarA(1u128); - - let data = test_with_enum.to_capnp_bytes(); - let test_with_enum2 = TestWithEnum::from_capnp_bytes(&data).unwrap(); - - assert_eq!(test_with_enum, test_with_enum2); -} diff --git a/components/channeler/src/lib.rs b/components/channeler/src/lib.rs index c786b5976..6570fc35a 100644 --- a/components/channeler/src/lib.rs +++ b/components/channeler/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/common/Cargo.toml b/components/common/Cargo.toml index 708f0314b..38d3c7392 100644 --- a/components/common/Cargo.toml +++ b/components/common/Cargo.toml @@ -8,7 +8,6 @@ edition = "2018" [dependencies] log = "0.4" - bytes = "0.5.4" futures = "0.3.1" @@ -17,7 +16,11 @@ byteorder = "1.1" backtrace = "0.3.14" base64 = "0.10.1" +uint = "0.8.5" + +quickcheck = "0.9.2" [dev-dependencies] serde = {version = "1.0.104", features = ["derive"]} +paste = "1.0.1" diff --git a/components/common/src/async_rpc.rs b/components/common/src/async_rpc.rs new file mode 100644 index 000000000..73fa0ea8d --- /dev/null +++ b/components/common/src/async_rpc.rs @@ -0,0 +1,145 @@ +use crate::conn::{BoxFuture, BoxStream}; +use futures::channel::oneshot; + +#[derive(Debug)] +pub enum OpError { + SendOpFailed, + ResponseOpFailed(oneshot::Canceled), +} + +pub type AsyncOpResult<'a, T> = BoxFuture<'a, Result>; +pub type AsyncOpStream<'a, T> = BoxStream<'a, Result>; + +/* +macro_rules! ops_enum_func { + ($variant_snake:ident($($arg_name: $arg_type),*) -> $ret_type) => { + paste! { + async fn $variant_snake(&mut self, $($arg_name: $arg_type),*) -> Result<$ret_type, OpError> { + let (op_sender, op_receiver) = oneshot::channel(); + let op = $op_enum::[<$variant_snake:camel>]($($arg_name),*, op_sender); + self.sender + .send(op) + .await + .map_err(|_| OpError::SendOpFailed)?; + op_receiver.await.map_err(OpError::ResponseOpFailed)? + } + } + }; + + ($variant_snake:ident($($arg_name: $arg_type),*)) => { + paste! { + async fn $variant_snake(&mut self, $($arg_name: $arg_type),*) { + let (op_sender, op_receiver) = oneshot::channel(); + let op = $op_enum::[<$variant_snake:camel>]($($arg_name),*, op_sender); + self.sender + .send(op) + .await + .map_err(|_| OpError::SendOpFailed)?; + op_receiver.await.map_err(OpError::ResponseOpFailed)? + } + } + }; +} +*/ + +// A helper macro, allowing to have functions with unit "()" return value. +#[doc(hidden)] +#[macro_export] +macro_rules! get_out_type { + ($ret_type:path) => { + $ret_type + }; + () => { + () + }; +} + +#[macro_export] +/// Create an async interface for request/response messages +/// Generates an enum of all possible RPC messages, and an async interface that knows how to send +/// those RPC messages. +macro_rules! ops_trait { + (($transaction_trait:ident $(<$($ty:ident),*>)?) => { + $( + $variant_snake:ident ($($($arg_name:ident: $arg_type:path),+)?) $(-> $ret_type:path)? + );* + // Possibly an extra semicolon: + $(;)? + }) => { + /* + paste! { + // Enum for all possible operations + pub enum $op_enum$(<$($ty),*>)? { + $( + [<$variant_snake:camel>]($($($arg_type),+ ,)? oneshot::Sender>) + ),* + } + } + */ + + // A transaction client + /* + pub struct $transaction$(<$($ty),*>)? { + sender: mpsc::Sender<$op_enum$(<$($ty),*>)?>, + } + */ + + pub trait $transaction_trait$(<$($ty),*>)? { + $( + paste! { + fn $variant_snake(&mut self $(, $($arg_name: $arg_type),+)?) -> BoxFuture<'static, Result< get_out_type!($($ret_type)?) , OpError>>; + } + )* + } + + /* + impl$(<$($ty),*>)? $transaction_trait$(<$($ty),*>)? { + pub fn new(sender: mpsc::Sender<$op_enum$(<$($ty),*>)?>) -> Self { + Self { sender } + } + $( + paste! { + pub async fn $variant_snake(&mut self $(, $($arg_name: $arg_type),+)?) -> Result< get_out_type!($($ret_type)?) , OpError> { + let (op_sender, op_receiver) = oneshot::channel(); + let op = $op_enum::[<$variant_snake:camel>]($($($arg_name),+,)? op_sender); + self.sender + .send(op) + .await + .map_err(|_| OpError::SendOpFailed)?; + op_receiver.await.map_err(OpError::ResponseOpFailed)? + } + } + )* + } + */ + }; + +} + +#[allow(dead_code)] +#[cfg(test)] +mod tests { + use super::*; + + // use futures::channel::mpsc; + // use futures::SinkExt; + use paste::paste; + + use crate::conn::BoxFuture; + + #[test] + fn test_rpc_enums() { + ops_trait!((TcTransaction1) => { + func1(hello: Option) -> u32; + func2() -> u8; + func3(); + func4(world: u64) -> u32; + func5(world: u64); + }); + + ops_trait!((TcTransaction2) => { + func1(hello: String) -> Result; + func2(world: String) -> u32 + }); + } +} diff --git a/components/common/src/lib.rs b/components/common/src/lib.rs index b7040b044..412ffb5cb 100644 --- a/components/common/src/lib.rs +++ b/components/common/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, @@ -14,9 +14,13 @@ extern crate log; #[cfg(test)] extern crate serde; +pub mod async_rpc; pub mod int_convert; pub mod never; pub mod safe_arithmetic; +pub mod u256; + +// TODO: Big array might not be needed: #[macro_use] pub mod big_array; #[macro_use] diff --git a/components/common/src/u256.rs b/components/common/src/u256.rs new file mode 100644 index 000000000..3a154787f --- /dev/null +++ b/components/common/src/u256.rs @@ -0,0 +1,34 @@ +use quickcheck::{Arbitrary, Gen}; +use uint::construct_uint; + +construct_uint! { + /// A 256 bit unsigned int + pub struct U256(4); +} + +impl Arbitrary for U256 { + fn arbitrary(g: &mut G) -> Self { + U256([ + u64::arbitrary(g), + u64::arbitrary(g), + u64::arbitrary(g), + u64::arbitrary(g), + ]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_u256_basic() { + let mut num = U256::from(1); + for _ in 0..255 { + num <<= 1; + assert!(!num.is_zero()); + } + num <<= 1; + assert!(num.is_zero()); + } +} diff --git a/components/connection/src/lib.rs b/components/connection/src/lib.rs index a37dd011a..5c7f8f2cf 100644 --- a/components/connection/src/lib.rs +++ b/components/connection/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/crypto/Cargo.toml b/components/crypto/Cargo.toml index c6dad3e8a..d2a68b5d4 100644 --- a/components/crypto/Cargo.toml +++ b/components/crypto/Cargo.toml @@ -16,6 +16,7 @@ ed25519-dalek = "1.0.0-pre.3" sha2 = "0.9.0" hkdf = "0.9.0-alpha.0" chacha20poly1305 = "0.5.1" +hmac = "0.9.0" serde = {version = "1.0.104", features = ["derive"]} @@ -25,8 +26,8 @@ base64 = "0.9" derive_more = "0.14.0" # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" rand = "0.7.3" rand_core = "0.5.1" diff --git a/components/crypto/src/dh.rs b/components/crypto/src/dh.rs index 44dcd4401..c628d2430 100644 --- a/components/crypto/src/dh.rs +++ b/components/crypto/src/dh.rs @@ -7,14 +7,42 @@ use crate::error::CryptoError; use crate::rand::CryptoRandom; use crate::sym_encrypt::SymmetricKey; -pub const SHARED_SECRET_LEN: usize = 32; +pub struct DhEphemeralPrivateKey(x25519_dalek::EphemeralSecret); +pub struct DhStaticPrivateKey(x25519_dalek::StaticSecret); -pub struct DhPrivateKey(x25519_dalek::EphemeralSecret); +/// A diffie hellman private key that we can reuse multiple times, and save to disk. +impl DhStaticPrivateKey { + /// Create a new ephemeral private key. + pub fn new(rng: &mut R) -> Result { + Ok(DhStaticPrivateKey(x25519_dalek::StaticSecret::new(rng))) + } + + /// Compute public key from our private key. + /// The public key will be sent to remote side. + pub fn compute_public_key(&self) -> Result { + Ok(DhPublicKey::from( + x25519_dalek::PublicKey::from(&self.0).as_bytes(), + )) + } + + // TODO: Implement handshake +} + +impl From<[u8; 32]> for DhStaticPrivateKey { + /// Load a static diffie hellman private key from an array of bytes + fn from(bytes: [u8; 32]) -> Self { + DhStaticPrivateKey(x25519_dalek::StaticSecret::from(bytes)) + } +} -impl DhPrivateKey { +/// An ephemeral diffie hellman private key. Can be used only once, and should not be saved to +/// disk. +impl DhEphemeralPrivateKey { /// Create a new ephemeral private key. - pub fn new(rng: &mut R) -> Result { - Ok(DhPrivateKey(x25519_dalek::EphemeralSecret::new(rng))) + pub fn new(rng: &mut R) -> Result { + Ok(DhEphemeralPrivateKey(x25519_dalek::EphemeralSecret::new( + rng, + ))) } /// Compute public key from our private key. @@ -25,6 +53,7 @@ impl DhPrivateKey { )) } + // TODO: Maybe separate handshake and kdf part? /// Derive a symmetric key from our private key and remote's public key. pub fn derive_symmetric_key( self, @@ -77,8 +106,8 @@ mod tests { #[test] fn test_derive_symmetric_key() { let mut rng = DummyRandom::new(&[1, 2, 3, 4, 5]); - let dh_private_a = DhPrivateKey::new(&mut rng).unwrap(); - let dh_private_b = DhPrivateKey::new(&mut rng).unwrap(); + let dh_private_a = DhEphemeralPrivateKey::new(&mut rng).unwrap(); + let dh_private_b = DhEphemeralPrivateKey::new(&mut rng).unwrap(); let public_key_a = dh_private_a.compute_public_key().unwrap(); let public_key_b = dh_private_b.compute_public_key().unwrap(); diff --git a/components/crypto/src/hash.rs b/components/crypto/src/hash.rs index d3ace45c9..6abbba6c7 100644 --- a/components/crypto/src/hash.rs +++ b/components/crypto/src/hash.rs @@ -1,16 +1,41 @@ use proto::crypto::HashResult; use sha2::{Digest, Sha512Trunc256}; -/// Calculate SHA512/256 over the given data. -pub fn sha_512_256(data: &[u8]) -> HashResult { - let mut hasher = Sha512Trunc256::new(); - hasher.update(data); - let digest_res = hasher.finalize(); +pub struct Hasher { + inner: Sha512Trunc256, +} + +impl Hasher { + pub fn new() -> Self { + Self { + inner: Sha512Trunc256::new(), + } + } + + pub fn update(&mut self, data: &[u8]) { + self.inner.update(data); + } + + pub fn chain(mut self, data: &[u8]) -> Self { + self.inner.update(data); + self + } - let mut inner = [0x00; HashResult::len()]; - inner.copy_from_slice(digest_res.as_ref()); + pub fn finalize(self) -> HashResult { + let digest_res = self.inner.clone().finalize(); - HashResult::from(&inner) + let mut inner = [0x00; HashResult::len()]; + inner.copy_from_slice(digest_res.as_ref()); + + HashResult::from(&inner) + } +} + +// TODO: Possibly remove from interface in the future, use Hasher instead. +// TODO: Possibly choose a more generic name, to allow changes in the future? +/// Calculate SHA512/256 over the given data. +pub fn hash_buffer(data: &[u8]) -> HashResult { + Hasher::new().chain(data).finalize() } #[cfg(test)] @@ -21,7 +46,7 @@ mod tests { fn hash_basic() { let data = b"This is a test!"; - let hash_res = sha_512_256(&data[..]); + let hash_res = hash_buffer(&data[..]); let expected = [ 0x34, 0x9c, 0x7e, 0xa7, 0x49, 0x8d, 0x04, 0x32, 0xdc, 0xb0, 0x60, 0x4a, 0x9e, 0xd3, @@ -31,4 +56,13 @@ mod tests { assert_eq!(hash_res.as_ref(), expected); } + + #[test] + fn hasher_empty() { + let x = Hasher::new().finalize(); + let y = Hasher::new().finalize(); + let z = Hasher::new().finalize(); + assert_eq!(x, y); + assert_eq!(y, z); + } } diff --git a/components/crypto/src/hmac.rs b/components/crypto/src/hmac.rs new file mode 100644 index 000000000..1fa155c93 --- /dev/null +++ b/components/crypto/src/hmac.rs @@ -0,0 +1,50 @@ +//! Hash based message authentication + +use hmac::{Hmac, Mac, NewMac}; +use proto::crypto::{HmacKey, HmacResult}; +use sha2::Sha512Trunc256; + +// Create alias for HMAC-SHA512-256 +type HmacSha512Trunc256 = Hmac; + +// TODO: Possibly add a more efficient version, with a struct and update() method. +// This might allow us to save allocation in the rest of the code. + +pub fn create_hmac(data: &[u8], hmac_key: &HmacKey) -> HmacResult { + let mut mac = HmacSha512Trunc256::new_varkey(&*hmac_key).expect("Invalid key length"); + mac.update(data); + let digest_res = mac.finalize().into_bytes(); + + let mut inner = [0x00; HmacResult::len()]; + inner.copy_from_slice(digest_res.as_ref()); + + HmacResult::from(inner) +} + +pub fn verify_hmac(data: &[u8], hmac_key: &HmacKey, hmac_result: &HmacResult) -> bool { + let mut mac = HmacSha512Trunc256::new_varkey(&*hmac_key).expect("Invalid key length"); + mac.update(data); + mac.verify(&hmac_result).is_ok() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hmac_basic() { + let my_data = b"This is my data"; + + let my_key1 = HmacKey::from([1u8; 32]); + let hmac_result1 = create_hmac(&my_data[..], &my_key1); + + let my_key2 = HmacKey::from([2u8; 32]); + let hmac_result2 = create_hmac(&my_data[..], &my_key2); + + assert!(verify_hmac(my_data, &my_key1, &hmac_result1)); + assert!(verify_hmac(my_data, &my_key2, &hmac_result2)); + + assert!(!verify_hmac(my_data, &my_key1, &hmac_result2)); + assert!(!verify_hmac(my_data, &my_key2, &hmac_result1)); + } +} diff --git a/components/crypto/src/identity.rs b/components/crypto/src/identity.rs index 9a70e0bc9..8dad8d0d9 100644 --- a/components/crypto/src/identity.rs +++ b/components/crypto/src/identity.rs @@ -3,13 +3,13 @@ use std::cmp::Ordering; use proto::crypto::{PrivateKey, PublicKey, Signature}; use crate::error::CryptoError; -use crate::hash::sha_512_256; +use crate::hash::hash_buffer; use crate::rand::{CryptoRandom, RandGen}; /// Check if one public key is "lower" than another. /// This is used to decide which side begins the token channel. pub fn compare_public_key(pk1: &PublicKey, pk2: &PublicKey) -> Ordering { - sha_512_256(pk1).cmp(&sha_512_256(pk2)) + hash_buffer(pk1).cmp(&hash_buffer(pk2)) } impl RandGen for PrivateKey { diff --git a/components/crypto/src/lib.rs b/components/crypto/src/lib.rs index d1bea9334..900bb8094 100644 --- a/components/crypto/src/lib.rs +++ b/components/crypto/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, @@ -16,6 +16,7 @@ pub mod dh; pub mod error; pub mod hash; pub mod hash_lock; +pub mod hmac; pub mod identity; // pub mod nonce_window; pub mod rand; diff --git a/components/crypto/src/rand.rs b/components/crypto/src/rand.rs index 898234112..58cc6c5d5 100644 --- a/components/crypto/src/rand.rs +++ b/components/crypto/src/rand.rs @@ -5,7 +5,7 @@ use rand_core::impls; use crate::error::CryptoError; -use proto::crypto::{InvoiceId, PaymentId, PlainLock, RandValue, Salt, Uid}; +use proto::crypto::{InvoiceId, NodePort, PaymentId, PlainLock, RandValue, Salt, Uid}; // TODO: Maybe we shouldn't have Sync + Send here as bounds? pub trait CryptoRandom: RngCore + CryptoRng { @@ -141,6 +141,14 @@ impl RandGen for InvoiceId { } } +impl RandGen for NodePort { + fn rand_gen(crypt_rng: &mut impl CryptoRandom) -> Self { + let mut res = Self::default(); + crypt_rng.fill(&mut res).unwrap(); + res + } +} + impl RandGen for RandValue { fn rand_gen(crypt_rng: &mut impl CryptoRandom) -> Self { let mut res = Self::default(); diff --git a/components/database/Cargo.toml b/components/database/Cargo.toml index 252ae8eab..d6ab988d7 100644 --- a/components/database/Cargo.toml +++ b/components/database/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] common = { path = "../common", version = "0.1.0", package = "offset-common" } +proto = { path = "../proto", version = "0.1.0", package = "offset-proto" } log = "0.4" futures = "0.3.1" @@ -18,8 +19,8 @@ atomicwrites = "0.2.2" serde = {version = "1.0.104", features = ["derive"]} base64 = "0.9" -# bincode = "1.1.2" serde_json = "1.0.44" +rusqlite = {version = "0.23.1", features = ["bundled"]} [dev-dependencies] diff --git a/components/database/src/create.rs b/components/database/src/create.rs new file mode 100644 index 000000000..d109acb57 --- /dev/null +++ b/components/database/src/create.rs @@ -0,0 +1,919 @@ +use rusqlite::{self, params, Connection}; + +/// Create a new Offset database +fn create_database(conn: &mut Connection) -> rusqlite::Result<()> { + let tx = conn.transaction()?; + // TODO: A better way to do this? + tx.execute("PRAGMA foreign_keys = ON;", params![])?; + + tx.execute( + "CREATE TABLE applications ( + app_public_key BLOB NOT NULL PRIMARY KEY, + app_name TEXT NOT NULL UNIQUE, + last_online INTEGER NOT NULL, + is_enabled BOOL NOT NULL, + + perm_info_docs BOOL NOT NULL, + perm_info_friends BOOL NOT NULL, + perm_buy BOOL NOT NULL, + perm_sell BOOL NOT NULL, + perm_config_card BOOL NOT NULL, + perm_config_access BOOL NOT NULL + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_applications ON applications(app_public_key, app_name);", + params![], + )?; + + // Single row is enforced in this table according to https://stackoverflow.com/a/33104119 + tx.execute( + "CREATE TABLE node( + id INTEGER PRIMARY KEY CHECK (id == 0), -- enforce single row + version INTEGER NOT NULL, -- Database version + local_public_key BLOB NOT NULL + );", + params![], + )?; + + tx.execute( + "CREATE TABLE friends( + friend_public_key BLOB NOT NULL PRIMARY KEY, + friend_name TEXT NOT NULL UNIQUE, + -- Friend's name + is_enabled BOOL NOT NULL, + -- Do we allow connectivity with this friend? + is_consistent BOOL NOT NULL + -- Is the channel with this friend consistent? + );", + params![], + )?; + + // friend public key index: + tx.execute( + "CREATE UNIQUE INDEX idx_friends_friend_public_key ON friends(friend_public_key);", + params![], + )?; + + // public name index: + tx.execute( + "CREATE UNIQUE INDEX idx_friends_name ON friends(friend_name);", + params![], + )?; + + // Relays list we have received from the friend + // We use those relays to connect to the friend. + tx.execute( + "CREATE TABLE received_friend_relays( + friend_public_key BLOB NOT NULL, + relay_public_key BLOB NOT NULL, + port BLOB NOT NULL, + address TEXT NOT NULL, + PRIMARY KEY(friend_public_key, relay_public_key), + FOREIGN KEY(friend_public_key) + REFERENCES friends(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_received_friend_relays ON received_friend_relays(friend_public_key, relay_public_key);", + params![], + )?; + + // Relays list we have sent to the friend + // The friend will use those relays to connect to us + tx.execute( + "CREATE TABLE sent_friend_relays( + friend_public_key BLOB NOT NULL, + relay_public_key BLOB NOT NULL, + port BLOB NOT NULL, + address TEXT NOT NULL, + is_remove BOOL NOT NULL, + -- Is marked for removal? + generation BLOB, + -- At which generation this relay was added/removed? + -- NULL if change was already acked. + CHECK (NOT (is_remove == true AND generation == NULL)), + PRIMARY KEY(friend_public_key, relay_public_key), + FOREIGN KEY(friend_public_key) + REFERENCES friends(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_sent_friend_relays ON sent_friend_relays(friend_public_key, relay_public_key);", + params![], + )?; + + tx.execute( + "CREATE INDEX idx_sent_friend_relays_generation ON sent_friend_relays(generation);", + params![], + )?; + + tx.execute( + "CREATE TABLE currency_configs( + friend_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + rate_mul INTEGER NOT NULL + CHECK(rate_mul >= 0 AND rate_mul < 0x100000000), + rate_add INTEGER NOT NULL + CHECK(rate_add >= 0 AND rate_add < 0x100000000), + remote_max_debt BLOB NOT NULL, + local_max_debt BLOB NOT NULL, + is_open BOOL NOT NULL, + is_remove BOOL NOT NULL, + -- Was marked for removal locally? + PRIMARY KEY(friend_public_key, currency), + FOREIGN KEY(friend_public_key) + REFERENCES friends(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_currency_configs ON currency_configs(friend_public_key, currency);", + params![], + )?; + + tx.execute( + "CREATE TABLE consistent_channels( + friend_public_key BLOB NOT NULL PRIMARY KEY, + is_consistent BOOL NOT NULL + CHECK (is_consistent == true) + DEFAULT true, + move_token_counter BLOB NOT NULL, + is_incoming BOOL NOT NULL, + + FOREIGN KEY(friend_public_key, is_consistent) + REFERENCES friends(friend_public_key, is_consistent) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_consistent_channels ON consistent_channels(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE consistent_channels_incoming( + friend_public_key BLOB NOT NULL PRIMARY KEY, + is_incoming BOOL NOT NULL + CHECK (is_incoming == true) + DEFAULT true, + old_token BLOB NOT NULL, + new_token BLOB NOT NULL, + + FOREIGN KEY(friend_public_key, is_incoming) + REFERENCES consistent_channels(friend_public_key, is_incoming) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_consistent_channels_incoming ON consistent_channels_incoming(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE consistent_channels_outgoing( + friend_public_key BLOB NOT NULL PRIMARY KEY, + is_incoming BOOL NOT NULL + CHECK (is_incoming == false) + DEFAULT false, + move_token_out BLOB NOT NULL, + + -- Do we save a previously received hashed incoming move token? + is_with_incoming BOOL NOT NULL, + + FOREIGN KEY(friend_public_key, is_incoming) + REFERENCES consistent_channels(friend_public_key, is_incoming) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_consistent_channels_outgoing ON consistent_channels_outgoing(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE consistent_channels_outgoing_with_incoming( + friend_public_key BLOB NOT NULL PRIMARY KEY, + is_with_incoming BOOL NOT NULL + CHECK (is_with_incoming == true) + DEFAULT true, + + -- Data saved for last incoming move token. + -- The list of balances for the last incoming move token is saved in a separate table. + old_token BLOB NOT NULL, + move_token_counter BLOB NOT NULL, + new_token BLOB NOT NULL, + + FOREIGN KEY(friend_public_key, is_with_incoming) + REFERENCES consistent_channels_outgoing(friend_public_key, is_with_incoming) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_consistent_channels_outgoing_with_incoming ON consistent_channels_outgoing_with_incoming(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE last_incoming_balances( + friend_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + + balance BLOB NOT NULL, + -- TODO: Pending credits be calculated automatically from the pending requests: + -- local_pending_debt BLOB NOT NULL, + -- remote_pending_debt BLOB NOT NULL, + in_fees BLOB NOT NULL, + out_fees BLOB NOT NULL, + + PRIMARY KEY(friend_public_key, currency), + FOREIGN KEY(friend_public_key) + REFERENCES consistent_channels_outgoing_with_incoming(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_last_incoming_balances ON last_incoming_balances(friend_public_key, currency);", + params![], + )?; + + tx.execute( + "CREATE TABLE inconsistent_channels( + friend_public_key BLOB NOT NULL PRIMARY KEY, + is_consistent BOOL NOT NULL + CHECK (is_consistent == false) + DEFAULT false, + -- Last seen incoming move token: + opt_move_token_in BLOB, + -- Local reset terms: + local_reset_token BLOB NOT NULL, + local_reset_move_token_counter BLOB NOT NULL, + FOREIGN KEY(friend_public_key, is_consistent) + REFERENCES friends(friend_public_key, is_consistent) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_inconsistent_channels ON inconsistent_channels(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE local_reset_terms( + friend_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + balance BLOB NOT NULL, + in_fees BLOB NOT NULL, + out_fees BLOB NOT NULL, + PRIMARY KEY(friend_public_key, currency), + FOREIGN KEY(friend_public_key) + REFERENCES inconsistent_channels(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX local_reset_terms_idx ON local_reset_terms(friend_public_key, currency);", + params![], + )?; + + // Inconsistent channels that contain remote reset terms. + tx.execute( + "CREATE TABLE inconsistent_channels_with_remote( + friend_public_key BLOB NOT NULL PRIMARY KEY, + -- Remote reset terms: + remote_reset_token BLOB NOT NULL, + remote_reset_move_token_counter BLOB NOT NULL, + FOREIGN KEY(friend_public_key) + REFERENCES inconsistent_channels(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX inconsistent_channels_with_remote_idx ON + inconsistent_channels_with_remote(friend_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE remote_reset_terms( + friend_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + balance BLOB NOT NULL, + in_fees BLOB NOT NULL, + out_fees BLOB NOT NULL, + PRIMARY KEY(friend_public_key, currency), + FOREIGN KEY(friend_public_key) + REFERENCES inconsistent_channels_with_remote(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX remote_reset_terms_idx ON remote_reset_terms(friend_public_key, currency);", + params![], + )?; + + tx.execute( + "CREATE TABLE mutual_credits( + friend_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + balance BLOB NOT NULL, + -- TODO: Pending credits be calculated automatically from the pending requests: + -- local_pending_debt BLOB NOT NULL, + -- remote_pending_debt BLOB NOT NULL, + in_fees BLOB NOT NULL, + out_fees BLOB NOT NULL, + PRIMARY KEY(friend_public_key, currency), + FOREIGN KEY(friend_public_key) + REFERENCES consistent_channels(friend_public_key) + ON DELETE CASCADE, + FOREIGN KEY(friend_public_key, currency) + REFERENCES currency_configs(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_mutual_credits ON mutual_credits(friend_public_key, currency);", + params![], + )?; + + // Requests that were sent through our node to a friend. + tx.execute( + "CREATE TABLE local_open_transactions( + request_id BLOB NOT NULL PRIMARY KEY, + currency TEXT NOT NULL, + friend_public_key BLOB NOT NULL, + route BLOB NOT NULL, + src_hashed_lock BLOB NOT NULL, + dest_payment BLOB NOT NULL, + total_dest_payment BLOB NOT NULL, + invoice_hash BLOB NOT NULL, + hmac BLOB NOT NULL, + left_fees BLOB NOT NULL, + FOREIGN KEY(friend_public_key, currency) + REFERENCES mutual_credits(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_local_open_transactions ON local_open_transactions(request_id);", + params![], + )?; + + // Requests that were sent through a friend along our node. + tx.execute( + "CREATE TABLE remote_open_transactions( + request_id BLOB NOT NULL PRIMARY KEY, + currency TEXT NOT NULL, + friend_public_key BLOB NOT NULL, + route BLOB NOT NULL, + src_hashed_lock BLOB NOT NULL, + dest_payment BLOB NOT NULL, + total_dest_payment BLOB NOT NULL, + invoice_hash BLOB NOT NULL, + hmac BLOB NOT NULL, + left_fees BLOB NOT NULL, + FOREIGN KEY(friend_public_key, currency) + REFERENCES mutual_credits(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_remote_open_transactions ON remote_open_transactions(request_id);", + params![], + )?; + + // Requests that were created locally + // (As opposed to requests that were received for the first time from a friend) + tx.execute( + "CREATE TABLE local_requests( + request_id BLOB NOT NULL PRIMARY KEY + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_local_requests ON local_requests(request_id);", + params![], + )?; + + tx.execute( + "CREATE TABLE pending_user_requests( + queue_index BLOB NOT NULL UNIQUE, + request_id BLOB NOT NULL PRIMARY KEY, + currency TEXT NOT NULL, + friend_public_key BLOB NOT NULL, + route BLOB NOT NULL, + src_hashed_lock BLOB NOT NULL, + dest_payment BLOB NOT NULL, + total_dest_payment BLOB NOT NULL, + invoice_hash BLOB NOT NULL, + hmac BLOB NOT NULL, + left_fees BLOB NOT NULL, + UNIQUE (friend_public_key, queue_index), + FOREIGN KEY(friend_public_key, currency) + REFERENCES mutual_credits(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_user_requests_request_id ON pending_user_requests(request_id);", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_user_requests_queue_index ON pending_user_requests(queue_index);", + params![], + )?; + + tx.execute( + "CREATE TABLE pending_requests( + queue_index BLOB NOT NULL, + request_id BLOB NOT NULL PRIMARY KEY, + currency TEXT NOT NULL, + friend_public_key BLOB NOT NULL, + route BLOB NOT NULL, + src_hashed_lock BLOB NOT NULL, + dest_payment BLOB NOT NULL, + total_dest_payment BLOB NOT NULL, + invoice_hash BLOB NOT NULL, + hmac BLOB NOT NULL, + left_fees BLOB NOT NULL, + UNIQUE (friend_public_key, queue_index), + FOREIGN KEY(friend_public_key, currency) + REFERENCES mutual_credits(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_requests_request_id ON pending_requests(request_id);", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_requests_friend_public_key_queue_index ON pending_requests(friend_public_key, queue_index);", + params![], + )?; + + tx.execute( + "CREATE TABLE pending_backwards( + queue_index BLOB NOT NULL UNIQUE, + request_id BLOB NOT NULL PRIMARY KEY, + currency BLOB NOT NULL, + friend_public_key BLOB NOT NULL, + backwards_type TEXT CHECK (backwards_type IN ('R', 'C')) NOT NULL, + -- R: Response, C: Cancel + UNIQUE (friend_public_key, queue_index), + FOREIGN KEY(friend_public_key, currency) + REFERENCES mutual_credits(friend_public_key, currency) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_backwards_request_id ON pending_backwards(request_id);", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_backwards_friend_public_key_queue_index ON pending_backwards(friend_public_key, queue_index);", + params![], + )?; + + tx.execute( + "CREATE TABLE pending_backwards_responses( + request_id BLOB NOT NULL PRIMARY KEY, + backwards_type TEXT NOT NULL + CHECK (backwards_type == 'R') + DEFAULT 'R', + src_plain_lock BLOB NOT NULL, + serial_num BLOB NOT NULL, + signature BLOB NOT NULL, + FOREIGN KEY(request_id, backwards_type) + REFERENCES pending_backwards(request_id, backwards_type) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_backwards_responses ON pending_backwards_responses(request_id);", + params![], + )?; + + tx.execute( + "CREATE TABLE pending_backwards_cancels( + request_id BLOB NOT NULL PRIMARY KEY, + backwards_type TEXT NOT NULL + CHECK (backwards_type == 'C') + DEFAULT 'C', + FOREIGN KEY(request_id, backwards_type) + REFERENCES pending_backwards(request_id, backwards_type) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_pending_backwards_cancels ON pending_backwards_cancels(request_id);", + params![], + )?; + + tx.execute( + "CREATE TABLE relays( + relay_public_key BLOB NOT NULL PRIMARY KEY, + address TEXT NOT NULL, + relay_name TEXT NOT NULL + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_relays ON relays(relay_public_key);", + params![], + )?; + + tx.execute( + "CREATE TABLE index_servers( + index_public_key BLOB NOT NULL PRIMARY KEY, + address TEXT NOT NULL, + index_name TEXT NOT NULL + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_index_servers ON index_servers(index_public_key);", + params![], + )?; + + // Ongoing payments that were not yet finalized + tx.execute( + "CREATE TABLE open_payments( + payment_id BLOB NOT NULL PRIMARY KEY, + app_public_key BLOB NOT NULL, + app_name TEXT NOT NULL, + dest_public_key BLOB NOT NULL, + currency TEXT NOT NULL, + amount BLOB NOT NULL, + description TEXT NOT NULL, + src_plain_lock BLOB NOT NULL, + fees BLOB, + -- fees == NULL if we don't know the fees yet. + FOREIGN KEY(app_public_key) + REFERENCES applications(app_public_key) + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_open_payments ON open_payments(payment_id);", + params![], + )?; + + // Payments where the user accepted the proposed fees. + // Such payments can only be cancelled by the seller. + tx.execute( + "CREATE TABLE open_payments_accepted( + payment_id BLOB NOT NULL PRIMARY KEY, + fees BLOB NOT NULL + CHECK (fees != NULL), + FOREIGN KEY(payment_id, fees) + REFERENCES open_payments(payment_id, fees) + ON DELETE CASCADE + );", + params![], + )?; + + // TODO: Add indexes. Should we add an index to `request_id` here? + tx.execute( + "CREATE TABLE open_payments_requests( + payment_id BLOB NOT NULL, + request_id BLOB NOT NULL UNIQUE, + received_ack BOOL NOT NULL, + -- Have we received an acknowledgement from the seller about this request? + PRIMARY KEY(payment_id, request_id), + FOREIGN KEY(payment_id) + REFERENCES open_payments_accepted(payment_id) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE TABLE open_invoices( + invoice_id BLOB NOT NULL PRIMARY KEY, + app_public_key BLOB NOT NULL, + psk BLOB NOT NULL, + description TEXT NOT NULL, + data_hash BLOB NOT NULL, + currency TEXT NOT NULL, + opt_amount BLOB, + FOREIGN KEY(app_public_key) + REFERENCES applications(app_public_key) + ON DELETE RESTRICT + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_open_invoices ON open_invoices(invoice_id);", + params![], + )?; + + tx.execute( + "CREATE TABLE open_invoices_relays( + invoice_id BLOB NOT NULL, + relay_address TEXT NOT NULL, + relay_port BLOB NOT NULL, + PRIMARY KEY(invoice_id, relay_address), + FOREIGN KEY(invoice_id) + REFERENCES open_invoices(invoice_id) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_open_invoices_relays ON open_invoices_relays(invoice_id, relay_address);", + params![], + )?; + + tx.execute( + "CREATE TABLE invoice_actions( + invoice_id BLOB NOT NULL, + action_id BLOB NOT NULL, + description TEXT NOT NULL, + invoice_hash BLOB NOT NULL UNIQUE, + -- invoiceHash = sha512/256(action_id || hash(description) || data_hash) + opt_src_hashed_lock BLOB, + -- src_hashed_lock is provided by the buyer/payer during the commitment. + PRIMARY KEY(invoice_id, action_id), + FOREIGN KEY(invoice_id) + REFERENCES open_invoices(invoice_id) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_invoice_actions ON invoice_actions(invoice_id, action_id);", + params![], + )?; + + tx.execute( + "CREATE TABLE invoice_actions_requests( + invoice_id BLOB NOT NULL, + action_id BLOB NOT NULL, + request_id BLOB NOT NULL PRIMARY KEY, + FOREIGN KEY(request_id) + REFERENCES remote_open_transactions(request_id) + ON DELETE RESTRICT, + FOREIGN KEY(invoice_id, action_id) + REFERENCES invoice_actions(invoice_id, action_id) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE INDEX idx_actions_requests ON invoice_actions_requests(invoice_id, action_id);", + params![], + )?; + + tx.execute( + "CREATE UNIQUE INDEX idx_actions_requests_request_id ON invoice_actions_requests(request_id);", + params![], + )?; + + // Currently open friend invites + tx.execute( + "CREATE TABLE friend_invites( + invite_id BLOB NOT NULL PRIMARY KEY, + psk BLOB NOT NULL, + invite_type TEXT CHECK (invite_type IN ('A', 'U')) NOT NULL, + -- Information about the application that created this invite + app_public_key BLOB NOT NULL, + app_name TEXT NOT NULL + );", + params![], + )?; + + // Invites that are used to add a new friend + tx.execute( + "CREATE TABLE friend_invites_add( + invite_id BLOB NOT NULL PRIMARY KEY, + invite_type TEXT NOT NULL + CHECK (invite_type == 'A') + DEFAULT 'A', + new_friend_name TEXT NOT NULL, + FOREIGN KEY(invite_id, invite_type) + REFERENCES friend_invites(invite_id, invite_type) + ON DELETE CASCADE + );", + params![], + )?; + + // Invites that are used to inform a remote friend about updated relays list. + tx.execute( + "CREATE TABLE friend_invites_update( + invite_id BLOB NOT NULL PRIMARY KEY, + invite_type TEXT NOT NULL + CHECK (invite_type == 'U') + DEFAULT 'U', + friend_public_key BLOB NOT NULL UNIQUE, + FOREIGN KEY(invite_id, invite_type) + REFERENCES friend_invites(invite_id, invite_type) + ON DELETE CASCADE, + FOREIGN KEY(friend_public_key) + REFERENCES friends(friend_public_key) + ON DELETE CASCADE + );", + params![], + )?; + + // Relays we use to listen for friend invites + tx.execute( + "CREATE TABLE friend_invites_relays( + invite_id BLOB NOT NULL, + relay_public_key BLOB NOT NULL, + port BLOB NOT NULL, + address TEXT NOT NULL, + -- If friend_public_key == NULL, it means we haven't + -- been contacted by the friend yet, or that we have created a specific + -- friend invite. + PRIMARY KEY(invite_id, relay_public_key), + FOREIGN KEY(invite_id) + REFERENCES friend_invites(invite_id) + ON DELETE RESTRICT + );", + params![], + )?; + + // Documents table, allowing total order on all items payments and invoices + tx.execute( + "CREATE TABLE events( + counter BLOB NOT NULL PRIMARY KEY, + time INTEGER NOT NULL, + event_type TEXT CHECK (event_type IN ('P', 'I', 'F')) NOT NULL, + -- Event type: P: Payment, I: Invoice, F: Friend + -- Information about the application that trigerred this event: + app_public_key BLOB NOT NULL, + app_name TEXT NOT NULL + );", + params![], + )?; + + // Balances table, showing the exact balance for every event. + // The numbers shown represent the numbers right after the event occurred. + tx.execute( + "CREATE TABLE event_balances( + counter BLOB NOT NULL, + currency TEXT NOT NULL, + amount BLOB NOT NULL, + in_fees BLOB NOT NULL, + out_fees BLOB NOT NULL, + + PRIMARY KEY(counter, currency) + FOREIGN KEY(counter) + REFERENCES events(counter) + ON DELETE CASCADE + );", + params![], + )?; + + tx.execute( + "CREATE TABLE payment_events( + counter BLOB NOT NULL PRIMARY KEY, + event_type TEXT CHECK (event_type == 'P') + DEFAULT 'P' + NOT NULL, + response_hash BLOB NOT NULL, + dest_public_key BLOB NOT NULL, + serial_num BLOB NOT NULL, + action_id BLOB NOT NULL, + currency TEXT NOT NULL, + amount BLOB NOT NULL, + description TEXT NOT NULL, + data_hash BLOB NOT NULL, + signature BLOB NOT NULL, + UNIQUE (dest_public_key, serial_num), + -- A node can not issue the same invoice sequence number twice + FOREIGN KEY(counter, event_type) + REFERENCES events(counter, event_type) + ON DELETE CASCADE + );", + params![], + )?; + + // TODO: Add relevant indexes? + // - Search using description? + // - Search using counter? + // - How to make interface more flexible? + // Allowing user to make interesting queries? + + tx.execute( + "CREATE TABLE invoice_events ( + counter BLOB NOT NULL PRIMARY KEY, + event_type TEXT CHECK (event_type == 'I') + DEFAULT 'I' + NOT NULL, + serial_num BLOB NOT NULL UNIQUE, + currency TEXT NOT NULL, + amount BLOB NOT NULL, + description TEXT NOT NULL, + data_hash BLOB NOT NULL, + FOREIGN KEY(counter, event_type) + REFERENCES events(counter, event_type) + ON DELETE CASCADE + );", + params![], + )?; + + // A case of friend inconsistency or friend removal + tx.execute( + "CREATE TABLE friend_events ( + counter BLOB NOT NULL PRIMARY KEY, + event_type TEXT CHECK (event_type == 'F') + DEFAULT 'F' + NOT NULL, + friend_public_key BLOB NOT NULL, + friend_name TEXT NOT NULL, + FOREIGN KEY(counter, event_type) + REFERENCES events(counter, event_type) + ON DELETE CASCADE + );", + params![], + )?; + + // Balances (per currency) in case of friend inconsistency event. + // Can be one of: 1. Channel reset, 2. Friend removal, 3. currency removal + tx.execute( + "CREATE TABLE friend_event_balances ( + counter BLOB NOT NULL, + currency TEXT NOT NULL, + old_balance BLOB NOT NULL, + old_in_fees BLOB NOT NULL, + old_out_fees BLOB NOT NULL, + new_balance BLOB NOT NULL, + new_in_fees BLOB NOT NULL, + new_out_fees BLOB NOT NULL, + PRIMARY KEY(counter, currency), + FOREIGN KEY(counter) + REFERENCES friend_inconsistency_events(counter) + ON DELETE CASCADE + );", + params![], + )?; + + tx.commit() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_db() { + let mut conn = Connection::open_in_memory().unwrap(); + create_database(&mut conn).unwrap(); + } +} diff --git a/components/database/src/interface/funder.rs b/components/database/src/interface/funder.rs new file mode 100644 index 000000000..f0a864315 --- /dev/null +++ b/components/database/src/interface/funder.rs @@ -0,0 +1,116 @@ +use serde::{Deserialize, Serialize}; + +use proto::app_server::messages::{NamedRelayAddress, RelayAddress}; +use proto::crypto::PublicKey; +use proto::funder::messages::{Currency, Rate}; + +use crate::interface::types::{DbOpResult, DbOpSenderResult}; + +use futures::channel::mpsc; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum SentLocalRelays { + NeverSent, + Transition(Vec>, Vec>), // (last sent, before last sent) + LastSent(Vec>), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CurrencyConfig { + /// Rate of forwarding transactions that arrived from this friend to any other friend + /// for a certain currency. + pub rate: Rate, + /// Credit frame for the remote side (Set by the user of this node) + /// The remote side does not know this value. + pub remote_max_debt: u128, + /// Can new requests be sent through the mutual credit with this friend? + pub is_open: bool, +} + +// TODO: Remove this unused hint later: +#[allow(unused)] +pub enum FunderDbOp { + GetLocalPublicKey(DbOpSenderResult), + GetFriendIsEnabled(PublicKey, DbOpSenderResult), + SetFriendIsEnabled(PublicKey, bool, DbOpSenderResult<()>), + GetFriendRemoteRelays(PublicKey, DbOpSenderResult>>), + SetFriendRemoteRelays(PublicKey, Vec>, DbOpSenderResult<()>), + GetFriendSentLocalRelays(PublicKey, DbOpSenderResult>>), + SetFriendSentLocalRelays(PublicKey, Vec>, DbOpSenderResult<()>), + GetFriendName(PublicKey, DbOpSenderResult), + SetFriendName(PublicKey, String, DbOpSenderResult<()>), + GetCurrencyConfig(PublicKey, DbOpSenderResult), + SetFriendCurrencyRate(PublicKey, Currency, Rate, DbOpSenderResult<()>), + SetFriendCurrencyRemoteMaxDebt(PublicKey, Currency, u128, DbOpSenderResult<()>), + SetFriendCurrencyIsOpen(PublicKey, Currency, bool, DbOpSenderResult<()>), +} + +// TODO: Remove this unused hint later: +#[allow(unused)] +pub struct FunderDbClient { + sender: mpsc::Sender>, +} + +// TODO: Remove this unused hint later: +#[allow(unused)] +impl FunderDbClient { + pub async fn local_public_key() -> PublicKey { + unimplemented!(); + } + pub async fn get_friend_remote_relays( + friend_public_key: PublicKey, + ) -> DbOpResult>> { + unimplemented!(); + } + pub async fn set_friend_remote_relays( + friend_public_key: PublicKey, + remote_relays: Vec>, + ) -> DbOpResult<()> { + unimplemented!(); + } + pub async fn get_friend_sent_local_relays( + friend_public_key: PublicKey, + ) -> DbOpResult> { + unimplemented!(); + } + pub async fn set_friend_sent_local_relays( + friend_public_key: PublicKey, + sent_local_relays: SentLocalRelays, + ) -> DbOpResult<()> { + unimplemented!(); + } + pub async fn get_friend_name(friend_public_key: PublicKey) -> DbOpResult { + unimplemented!(); + } + pub async fn set_friend_name(friend_public_key: PublicKey, name: String) -> DbOpResult<()> { + unimplemented!(); + } + pub async fn get_friend_currency_config( + friend_public_key: PublicKey, + currency: Currency, + ) -> DbOpResult { + unimplemented!(); + } + pub async fn set_friend_currency_rate( + friend_public_key: PublicKey, + currency: Currency, + rate: Rate, + ) -> DbOpResult<()> { + unimplemented!(); + } + pub async fn set_friend_currency_remote_max_debt( + friend_public_key: PublicKey, + currency: Currency, + remote_max_debt: u128, + ) -> DbOpResult<()> { + unimplemented!(); + } + + async fn set_friend_currency_is_open( + friend_public_key: PublicKey, + currency: Currency, + is_open: bool, + ) -> DbOpResult<()> { + unimplemented!(); + } +} diff --git a/components/database/src/interface/mod.rs b/components/database/src/interface/mod.rs new file mode 100644 index 000000000..dd322a6a3 --- /dev/null +++ b/components/database/src/interface/mod.rs @@ -0,0 +1,2 @@ +pub mod funder; +mod types; diff --git a/components/database/src/interface/types.rs b/components/database/src/interface/types.rs new file mode 100644 index 000000000..f33dc4686 --- /dev/null +++ b/components/database/src/interface/types.rs @@ -0,0 +1,10 @@ +use futures::channel::oneshot; + +#[derive(Debug)] +pub enum DbOpError { + // SendOpFailed, +// ResponseOpFailed(oneshot::Canceled), +} + +pub type DbOpResult = Result; +pub type DbOpSenderResult = oneshot::Sender>; diff --git a/components/database/src/lib.rs b/components/database/src/lib.rs index 66d5f1e46..26abd9e0d 100644 --- a/components/database/src/lib.rs +++ b/components/database/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, @@ -13,8 +13,12 @@ extern crate serde; mod atomic_db; +#[allow(unused)] +mod create; mod database; pub mod file_db; +pub mod interface; +pub mod transaction; pub use self::atomic_db::AtomicDb; pub use self::database::{database_loop, DatabaseClient, DatabaseClientError, DatabaseRequest}; diff --git a/components/database/src/transaction.rs b/components/database/src/transaction.rs new file mode 100644 index 000000000..6f705cb4e --- /dev/null +++ b/components/database/src/transaction.rs @@ -0,0 +1,33 @@ +use futures::future::BoxFuture; + +// TODO: This is a compromise. We would have preferred to use a closure, but the lifetimes with the +// Transaction traits seem to not work out. +// See: https://users.rust-lang.org/t/returning-this-value-requires-that-1-must-outlive-2/51417 +/// A transaction function +pub trait TransFunc { + /// Input (Will be handed as shared reference) + type InRef; + /// Output + type Out; + /// Call invocation + fn call<'a>(self, input: &'a mut Self::InRef) -> BoxFuture<'a, Self::Out> + where + Self: 'a; +} + +/// A database transaction. Enforced using closure syntax. +/// Supports nested transactions. +pub trait Transaction +where + Self: std::marker::Sized, +{ + /// Begin a new transaction. + /// Transaction ends at the end of the closure scope. + /// If the returned value is Ok(..), the transaction was successful. Otherwise, the transaction + /// was canceled. + fn transaction<'a, F, T, E>(&'a mut self, f: F) -> BoxFuture<'a, Result> + where + F: TransFunc> + Send + 'a, + T: Send, + E: Send; +} diff --git a/components/funder/Cargo.toml b/components/funder/Cargo.toml index 5804c7f74..80a56f155 100644 --- a/components/funder/Cargo.toml +++ b/components/funder/Cargo.toml @@ -17,6 +17,7 @@ database = { path = "../database", version = "0.1.0", package = "offset-database log = "0.4" pretty_env_logger = "0.2" +derive_more = "0.15.0" bytes = "0.5.4" futures = "0.3.1" @@ -28,11 +29,13 @@ im = {version = "14.1.0", features = ["serde", "quickcheck"]} byteorder = {version = "1.1", features = ["i128"]} # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_macros = {version = "0.8"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" +quickcheck_macros = "0.9.1" rand = {version = "0.7.2"} +paste = "1.0.1" + [dev-dependencies] futures = {version = "0.3.1", features = ["thread-pool"]} diff --git a/components/funder/src/freeze_guard_legacy.rs b/components/funder/src/freeze_guard_legacy.rs deleted file mode 100644 index 2e56fde2e..000000000 --- a/components/funder/src/freeze_guard_legacy.rs +++ /dev/null @@ -1,528 +0,0 @@ -/* -use std::collections::HashMap as ImHashMap; - -use num_bigint::BigUint; - -use crypto::identity::PublicKey; -use crypto::hash::{HashResult, sha_512_256}; - -use common::int_convert::usize_to_u32; -use signature::canonical::CanonicalSerialize; - -use proto::funder::messages::{Ratio, FriendsRoute, FreezeLink}; - -use crate::credit_calc::CreditCalculator; -use crate::state::FunderState; -use crate::friend::ChannelStatus; - - -#[derive(Clone)] -struct FriendFreezeGuard { - frozen_credits_from: ImHashMap> - // ^-A ^-hash(route) ^-frozen -} - -impl FriendFreezeGuard { - fn new() -> FriendFreezeGuard { - FriendFreezeGuard { - frozen_credits_from: ImHashMap::new(), - } - } -} - -#[derive(Clone)] -pub struct FreezeGuard { - local_public_key: PublicKey, - // Total amount of credits frozen from A to B through this Offset node. - // ``` - // A --> ... --> X --> B - // ``` - // A could be any node, B must be a friend of this Offset node. - frozen_to: ImHashMap, - // ^ B -} - -#[derive(Debug)] -pub enum FreezeGuardMutation { - AddFrozenCredit((FriendsRoute, u128)), // (friends_route, dest_payment) - SubFrozenCredit((FriendsRoute, u128)), // (friends_route, dest_payment) -} - -fn hash_subroute(subroute: &[PublicKey]) -> HashResult { - let mut hash_buffer = Vec::new(); - - for public_key in subroute { - hash_buffer.extend_from_slice(&public_key); - } - sha_512_256(&hash_buffer) -} - -impl FreezeGuard { - pub fn new(local_public_key: &PublicKey) -> FreezeGuard { - FreezeGuard { - local_public_key: local_public_key.clone(), - frozen_to: ImHashMap::new(), - } - } - - pub fn mutate(&mut self, mutation: &FreezeGuardMutation) { - match mutation { - FreezeGuardMutation::AddFrozenCredit((friends_route, dest_payment)) => - self.add_frozen_credit(friends_route, *dest_payment), - FreezeGuardMutation::SubFrozenCredit((friends_route, dest_payment)) => - self.sub_frozen_credit(friends_route, *dest_payment), - } - } - - // TODO: Should be moved outside of this structure implementation. - // The only public function that allows mutation FreezeGuard should be mutate. - pub fn load_funder_state(mut self, funder_state: &FunderState) -> FreezeGuard - where - A: CanonicalSerialize + Clone, - { - // Local public key should match: - assert_eq!(self.local_public_key, funder_state.local_public_key); - for (_friend_public_key, friend) in &funder_state.friends { - if let ChannelStatus::Consistent(token_channel) = &friend.channel_status { - let pending_local_requests = &token_channel - .get_mutual_credit() - .state() - .pending_requests - .pending_local_requests; - for (_request_id, pending_request) in pending_local_requests { - self.add_frozen_credit(&pending_request.route, pending_request.dest_payment); - } - } - } - self - } - // TODO: Possibly refactor similar code of add/sub frozen credit to be one function that - // returns an iterator? - /// ```text - /// A -- ... -- X -- B - /// ``` - /// On the image: X is the local public key, B is a direct friend of X. - /// For every node Y along the route, we add the credits Y needs to freeze because of the route - /// from A to B. - fn add_frozen_credit(&mut self, route: &FriendsRoute, dest_payment: u128) { - assert!(route.public_keys.contains(&self.local_public_key)); - if &self.local_public_key == route.public_keys.last().unwrap() { - // We are the destination. Nothing to do here. - return; - } - - let my_index = route.pk_to_index(&self.local_public_key).unwrap(); - - let route_len = usize_to_u32(route.len()).unwrap(); - let credit_calc = CreditCalculator::new(route_len, dest_payment); - - let next_index = my_index.checked_add(1).unwrap(); - let next_public_key = route.index_to_pk(next_index).unwrap().clone(); - let friend_freeze_guard = self.frozen_to - .entry(next_public_key) - .or_insert_with(FriendFreezeGuard::new); - - // Iterate over all nodes from the beginning of the route until our index: - for node_index in 0 ..= my_index { - let node_public_key = route.index_to_pk(node_index).unwrap(); - - let next_node_index = usize_to_u32(node_index.checked_add(1).unwrap()).unwrap(); - let credits_to_freeze = credit_calc.credits_to_freeze(next_node_index).unwrap(); - let routes_map = friend_freeze_guard - .frozen_credits_from - .entry(node_public_key.clone()) - .or_insert_with(ImHashMap::new); - - let route_hash = hash_subroute(&route.public_keys[node_index ..= next_index]); - let route_entry = routes_map - .entry(route_hash.clone()) - .or_insert(0); - - *route_entry = (*route_entry).checked_add(credits_to_freeze).unwrap(); - } - } - - /// ```text - /// A -- ... -- X -- B - /// ``` - /// On the image: X is the local public key, B is a direct friend of X. - /// For every node Y along the route, we subtract the credits Y needs to freeze because of the - /// route from A to B. In other words, this method unfreezes frozen credits along this route. - fn sub_frozen_credit(&mut self, route: &FriendsRoute, dest_payment: u128) { - assert!(route.public_keys.contains(&self.local_public_key)); - if &self.local_public_key == route.public_keys.last().unwrap() { - // We are the destination. Nothing to do here. - return; - } - - let my_index = route.pk_to_index(&self.local_public_key).unwrap(); - - let route_len = usize_to_u32(route.len()).unwrap(); - let credit_calc = CreditCalculator::new(route_len, dest_payment); - - let next_index = my_index.checked_add(1).unwrap(); - let next_public_key = route - .index_to_pk(next_index).unwrap().clone(); - let friend_freeze_guard = self.frozen_to - .get_mut(&next_public_key) - .unwrap(); - - // Iterate over all nodes from the beginning of the route until our index: - for node_index in 0 ..= my_index { - let node_public_key = route - .index_to_pk(node_index) - .unwrap(); - - let next_node_index = usize_to_u32(node_index.checked_add(1).unwrap()).unwrap(); - let credits_to_freeze = credit_calc.credits_to_freeze(next_node_index).unwrap(); - let routes_map = friend_freeze_guard - .frozen_credits_from - .get_mut(&node_public_key) - .unwrap(); - - let route_hash = hash_subroute(&route.public_keys[node_index ..= next_index]); - let route_entry = routes_map - .get_mut(&route_hash) - .unwrap(); - - *route_entry = (*route_entry).checked_sub(credits_to_freeze).unwrap(); - // Cleanup: - if *route_entry == 0 { - routes_map.remove(&route_hash); - } - - // Cleanup: - if routes_map.is_empty() { - friend_freeze_guard.frozen_credits_from.remove(&node_public_key); - } - } - - // Cleanup: - if friend_freeze_guard.frozen_credits_from.is_empty() { - self.frozen_to.remove(&next_public_key); - } - } - - /// Get the amount of credits frozen from to going through this sub-route, - /// where is a friend of this Offset node. - fn get_frozen(&self, subroute: &[PublicKey]) -> u128 { - if subroute.len() < 2 { - unreachable!(); - } - let from_pk = &subroute[0]; - let to_pk = &subroute[subroute.len().checked_sub(1).unwrap()]; - - self.frozen_to.get(to_pk) - .and_then(|ref friend_freeze_guard| - friend_freeze_guard.frozen_credits_from.get(from_pk)) - .and_then(|ref routes_map| - routes_map.get(&hash_subroute(subroute)).cloned()) - .unwrap_or(0u128) - } - - /// ```text - /// A -- ... -- X -- B - /// ``` - /// X is the local public key. B is a direct friend of X. - /// For any node Y along the route from A to X, we make sure that B does not freeze too many - /// credits for this node. In other words, we save Y from freezing too many credits for B. - pub fn verify_freezing_links(&self, route: &FriendsRoute, - dest_payment: u128, - freeze_links: &[FreezeLink]) -> Option<()> { - assert!(freeze_links.len().checked_add(1).unwrap() <= route.len()); - let my_index = route.pk_to_index(&self.local_public_key).unwrap(); - // TODO: Do we ever get here as the destination of the route? - let next_index = my_index.checked_add(1).unwrap(); - assert_eq!(next_index, freeze_links.len()); - - let route_len = usize_to_u32(route.len()).unwrap(); - let credit_calc = CreditCalculator::new(route_len, dest_payment); - - let two_pow_128 = BigUint::new(vec![0x0u32, 0x0u32, 0x0u32, 0x0u32, 0x1u32]); - assert_eq!(two_pow_128, BigUint::from(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffffu128) + BigUint::from(1u128)); - - // Verify previous freezing links - for node_findex in 0 .. freeze_links.len() { - let first_freeze_link = &freeze_links[node_findex]; - let mut allowed_credits: BigUint = first_freeze_link.shared_credits.into(); - for freeze_link in &freeze_links[ - node_findex .. freeze_links.len()] { - - allowed_credits = match freeze_link.usable_ratio { - Ratio::One => allowed_credits, - Ratio::Numerator(num) => { - let b_num = BigUint::from(num); - allowed_credits * b_num / &two_pow_128 - }, - }; - } - - let subroute = &route - .public_keys[node_findex ..= next_index]; - let old_frozen = self.get_frozen(subroute); - - let node_findex = usize_to_u32(node_findex).unwrap(); - let next_node_findex = node_findex.checked_add(1).unwrap(); - let new_frozen = credit_calc.credits_to_freeze(next_node_findex)? - .checked_add(old_frozen).unwrap(); - if allowed_credits < new_frozen.into() { - return None; - } - } - Some(()) - } -} - - -#[cfg(test)] -mod tests { - use super::*; - use crypto::identity::{PublicKey, PublicKey::len()}; - - /// Get the amount of credits to be frozen on a route of a certain length - /// with certain amount to pay. - /// index is the location of the next node. For example, index = 1 will return the amount of - /// credits node 0 should freeze. - fn credit_freeze(route_len: u32, dest_payment: u128, index: u32) -> u128 { - CreditCalculator::new(route_len, dest_payment).credits_to_freeze(index).unwrap() - } - - #[test] - fn test_get_frozen_basic() { - /* - * a -- b -- (c) -- d - */ - - let pk_a = PublicKey::from(&[0xaa; PublicKey::len()]); - let pk_b = PublicKey::from(&[0xbb; PublicKey::len()]); - let pk_c = PublicKey::from(&[0xcc; PublicKey::len()]); - let pk_d = PublicKey::from(&[0xdd; PublicKey::len()]); - - let mut freeze_guard = FreezeGuard::new(&pk_c); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 19); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,19,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,19,2), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,19,3), guard_frozen); - - freeze_guard.sub_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 19); - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(guard_frozen, 0); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(guard_frozen, 0); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(guard_frozen, 0); - } - - #[test] - fn test_get_frozen_multiple_requests() { - /* - * a -- b -- (c) -- d - */ - - let pk_a = PublicKey::from(&[0xaa; PublicKey::len()]); - let pk_b = PublicKey::from(&[0xbb; PublicKey::len()]); - let pk_c = PublicKey::from(&[0xcc; PublicKey::len()]); - let pk_d = PublicKey::from(&[0xdd; PublicKey::len()]); - - let mut freeze_guard = FreezeGuard::new(&pk_c); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 11); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 17); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,2) + credit_freeze(3,17,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,3) + credit_freeze(3,17,2), guard_frozen); - - freeze_guard.sub_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 11); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(3,17,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(3,17,2), guard_frozen); - - freeze_guard.sub_frozen_credit(&FriendsRoute - {public_keys: vec![pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 17); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - } - - #[test] - fn test_get_frozen_branch_out() { - /* - * a -- b -- (c) -- d - * | - * e - */ - - let pk_a = PublicKey::from(&[0xaa; PublicKey::len()]); - let pk_b = PublicKey::from(&[0xbb; PublicKey::len()]); - let pk_c = PublicKey::from(&[0xcc; PublicKey::len()]); - let pk_d = PublicKey::from(&[0xdd; PublicKey::len()]); - let pk_e = PublicKey::from(&[0xee; PublicKey::len()]); - - let mut freeze_guard = FreezeGuard::new(&pk_c); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 11); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_e.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 17); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_e.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,17,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,2) + credit_freeze(4,17,2), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,3) + credit_freeze(4,17,3), guard_frozen); - - freeze_guard.sub_frozen_credit(&FriendsRoute - {public_keys: vec![pk_e.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 17); - - let guard_frozen = freeze_guard.get_frozen(&[pk_e.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,1), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,2), guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(credit_freeze(4,11,3), guard_frozen); - - freeze_guard.sub_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 11); - - let guard_frozen = freeze_guard.get_frozen(&[pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - - let guard_frozen = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - assert_eq!(0, guard_frozen); - } - - #[test] - fn test_verify_freezing_links_basic() { - /* - * a -- b -- (c) -- d - * | - * e - */ - - let pk_a = PublicKey::from(&[0xaa; PublicKey::len()]); - let pk_b = PublicKey::from(&[0xbb; PublicKey::len()]); - let pk_c = PublicKey::from(&[0xcc; PublicKey::len()]); - let pk_d = PublicKey::from(&[0xdd; PublicKey::len()]); - let pk_e = PublicKey::from(&[0xee; PublicKey::len()]); - - let mut freeze_guard = FreezeGuard::new(&pk_c); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_a.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 11); - freeze_guard.add_frozen_credit(&FriendsRoute - {public_keys: vec![pk_e.clone(), pk_b.clone(), pk_c.clone(), pk_d.clone()]}, 17); - - let frozen_b = freeze_guard.get_frozen(&[pk_b.clone(), pk_c.clone(), pk_d.clone()]); - let frozen_c = freeze_guard.get_frozen(&[pk_c.clone(), pk_d.clone()]); - let half = Ratio::Numerator(0x8000_0000_0000_0000_0000_0000_0000_0000); - - - // -- Freezing not allowed, c -- d - let route = FriendsRoute {public_keys: vec![pk_c.clone(), pk_d.clone()]}; - let freeze_link_c = FreezeLink { - shared_credits: (frozen_c + credit_freeze(2,9,1) - 1)* 2, - usable_ratio: half.clone(), // Half - }; - let freeze_links = vec![freeze_link_c]; - let res = freeze_guard.verify_freezing_links(&route, 9, &freeze_links); - assert!(res.is_none()); - - - // -- Freezing allowed: c -- d - let route = FriendsRoute {public_keys: vec![pk_c.clone(), pk_d.clone()]}; - let freeze_link_c = FreezeLink { - shared_credits: (frozen_c + credit_freeze(2,9,1)) * 2, - usable_ratio: half.clone(), // Half - }; - let freeze_links = vec![freeze_link_c]; - let res = freeze_guard.verify_freezing_links(&route, 9, &freeze_links); - assert!(res.is_some()); - - - // -- Freezing not allowed: b -- c -- d - let route = FriendsRoute {public_keys: vec![pk_b.clone(), pk_c.clone(), pk_d.clone()]}; - let freeze_link_b = FreezeLink { - shared_credits: (frozen_b + credit_freeze(3,9,1) - 1) * 4, // <-- should be not enough - usable_ratio: half.clone(), // Half - }; - let freeze_link_c = FreezeLink { - shared_credits: (frozen_c + credit_freeze(3,9,2)) * 2, - usable_ratio: half.clone(), // Half - }; - let freeze_links = vec![freeze_link_b, freeze_link_c]; - let res = freeze_guard.verify_freezing_links(&route, 9, &freeze_links); - assert!(res.is_none()); - - // -- Freezing not allowed: b -- c -- d - let route = FriendsRoute {public_keys: vec![pk_b.clone(), pk_c.clone(), pk_d.clone()]}; - let freeze_link_b = FreezeLink { - shared_credits: (frozen_b + credit_freeze(3,9,1)) * 4, - usable_ratio: half.clone(), // Half - }; - let freeze_link_c = FreezeLink { - shared_credits: (frozen_c + credit_freeze(3,9,2) - 1) * 2, // <-- should be not enough - usable_ratio: half.clone(), // Half - }; - let freeze_links = vec![freeze_link_b, freeze_link_c]; - let res = freeze_guard.verify_freezing_links(&route, 9, &freeze_links); - assert!(res.is_none()); - - - // -- Freezing allowed: b -- c -- d - let route = FriendsRoute {public_keys: vec![pk_b.clone(), pk_c.clone(), pk_d.clone()]}; - let freeze_link_b = FreezeLink { - shared_credits: (frozen_b + credit_freeze(3,9,1)) * 4, - usable_ratio: half.clone(), // Half - }; - let freeze_link_c = FreezeLink { - shared_credits: (frozen_c + credit_freeze(3,9,2)) * 2, - usable_ratio: half.clone(), // Half - }; - let freeze_links = vec![freeze_link_b, freeze_link_c]; - let res = freeze_guard.verify_freezing_links(&route, 9, &freeze_links); - assert!(res.is_some()); - } -} -*/ diff --git a/components/funder/src/lib.rs b/components/funder/src/lib.rs index 3218fe01e..50d8dadc1 100644 --- a/components/funder/src/lib.rs +++ b/components/funder/src/lib.rs @@ -1,7 +1,7 @@ #![crate_type = "lib"] #![cfg_attr(not(feature = "cargo-clippy"), allow(unknown_lints))] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, @@ -9,6 +9,7 @@ clippy::new_without_default )] +#[allow(unused)] #[macro_use] extern crate log; @@ -18,19 +19,25 @@ extern crate serde; #[macro_use] extern crate quickcheck_derive; -mod ephemeral; -mod friend; -mod funder; -mod handler; +// mod ephemeral; +// mod friend; +// mod funder; +// mod handler; +#[allow(unused)] mod liveness; +#[allow(unused)] mod mutual_credit; -pub mod report; -mod state; +mod route; +// pub mod report; +// mod state; +#[allow(unused)] +mod router; +#[allow(unused)] mod token_channel; pub mod types; -#[cfg(test)] -mod tests; +// #[cfg(test)] +// mod tests; -pub use self::funder::{funder_loop, FunderError}; -pub use self::state::{FunderMutation, FunderState}; +// pub use self::funder::{funder_loop, FunderError}; +// pub use self::state::{FunderMutation, FunderState}; diff --git a/components/funder/src/liveness.rs b/components/funder/src/liveness.rs index 05f626d71..6486640ff 100644 --- a/components/funder/src/liveness.rs +++ b/components/funder/src/liveness.rs @@ -2,17 +2,11 @@ use im::hashset::HashSet as ImHashSet; use proto::crypto::PublicKey; -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct Liveness { pub friends: ImHashSet, } -#[derive(Debug)] -pub enum LivenessMutation { - SetOnline(PublicKey), - SetOffline(PublicKey), -} - impl Liveness { pub fn new() -> Liveness { Liveness { @@ -20,19 +14,16 @@ impl Liveness { } } - pub fn mutate(&mut self, mutation: &LivenessMutation) { - match mutation { - LivenessMutation::SetOnline(public_key) => { - self.friends.insert(public_key.clone()); - } - LivenessMutation::SetOffline(public_key) => { - let _ = self.friends.remove(public_key); - } - } + pub fn set_online(&mut self, public_key: PublicKey) -> bool { + self.friends.insert(public_key).is_none() + } + + pub fn set_offline(&mut self, public_key: &PublicKey) -> bool { + self.friends.remove(&public_key).is_some() } - pub fn is_online(&self, friend_public_key: &PublicKey) -> bool { - self.friends.contains(&friend_public_key) + pub fn is_online(&self, public_key: &PublicKey) -> bool { + self.friends.contains(public_key) } } @@ -51,37 +42,37 @@ mod tests { assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOnline(pk_a.clone())); + liveness.set_online(pk_a.clone()); assert!(liveness.is_online(&pk_a)); assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOnline(pk_a.clone())); + liveness.set_online(pk_a.clone()); assert!(liveness.is_online(&pk_a)); assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOnline(pk_b.clone())); + liveness.set_online(pk_b.clone()); assert!(liveness.is_online(&pk_a)); assert!(liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOffline(pk_c.clone())); + liveness.set_offline(&pk_c); assert!(liveness.is_online(&pk_a)); assert!(liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOffline(pk_b.clone())); + liveness.set_offline(&pk_b); assert!(liveness.is_online(&pk_a)); assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOffline(pk_b.clone())); + liveness.set_offline(&pk_b); assert!(liveness.is_online(&pk_a)); assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); - liveness.mutate(&LivenessMutation::SetOffline(pk_a.clone())); + liveness.set_offline(&pk_a); assert!(!liveness.is_online(&pk_a)); assert!(!liveness.is_online(&pk_b)); assert!(!liveness.is_online(&pk_c)); diff --git a/components/funder/src/mutual_credit/incoming.rs b/components/funder/src/mutual_credit/incoming.rs index b8bb68dc0..fc04696c8 100644 --- a/components/funder/src/mutual_credit/incoming.rs +++ b/components/funder/src/mutual_credit/incoming.rs @@ -1,392 +1,304 @@ +use derive_more::From; + use crypto::hash_lock::HashLock; use crypto::identity::verify_signature; +use common::async_rpc::OpError; use common::safe_arithmetic::SafeSignedArithmetic; -use proto::funder::messages::{ - CancelSendFundsOp, CollectSendFundsOp, FriendTcOp, PendingTransaction, RequestSendFundsOp, - ResponseSendFundsOp, TransactionStage, -}; +use proto::crypto::PublicKey; +use proto::funder::messages::Currency; + use signature::signature_buff::create_response_signature_buffer; -use crate::types::create_pending_transaction; +use crate::route::Route; -use super::types::{McMutation, MutualCredit}; +use crate::mutual_credit::types::{ + McCancel, McDbClient, McOp, McRequest, McResponse, PendingTransaction, +}; +use crate::mutual_credit::utils::{mc_response_signature_buffer, response_op_from_mc_response}; +// TODO: Possibly rename to something shorter. +// TODO: Do we ever need the `pending_transaction` part for anything? +// If not, maybe we can remove it? #[derive(Debug)] pub struct IncomingResponseSendFundsOp { + // TODO: Possibly rename to pending_request? pub pending_transaction: PendingTransaction, - pub incoming_response: ResponseSendFundsOp, + pub incoming_response: McResponse, } #[derive(Debug)] pub struct IncomingCancelSendFundsOp { + // TODO: Possibly rename to pending_request? pub pending_transaction: PendingTransaction, - pub incoming_cancel: CancelSendFundsOp, -} - -#[derive(Debug)] -pub struct IncomingCollectSendFundsOp { - pub pending_transaction: PendingTransaction, - pub incoming_collect: CollectSendFundsOp, + // TODO: incoming_cancel looks redundant, because we can already deduce request_id from + // `pending_transaction`. Maybe remove it? + pub incoming_cancel: McCancel, } #[derive(Debug)] pub enum IncomingMessage { - Request(RequestSendFundsOp), - RequestCancel(RequestSendFundsOp), + Request(McRequest), + RequestCancel(McRequest), Response(IncomingResponseSendFundsOp), Cancel(IncomingCancelSendFundsOp), - Collect(IncomingCollectSendFundsOp), -} - -/// Resulting tasks to perform after processing an incoming operation. -pub struct ProcessOperationOutput { - pub incoming_message: Option, - pub mc_mutations: Vec, -} - -#[derive(Debug)] -pub enum ProcessOperationError { - /// The Route contains some public key twice. - InvalidRoute, - CreditsCalcOverflow, - RequestAlreadyExists, - RequestDoesNotExist, - InvalidResponseSignature, - NotExpectingResponse, - InvalidSrcPlainLock, - InvalidDestPlainLock, - NotExpectingCollect, - DestPaymentExceedsTotal, } #[derive(Debug)] -pub struct ProcessTransListError { - index: usize, - process_trans_error: ProcessOperationError, +pub enum ProcessOutput { + Incoming(IncomingMessage), + Inconsistency, } -pub fn process_operations_list( - mutual_credit: &mut MutualCredit, - operations: Vec, - remote_max_debt: u128, -) -> Result, ProcessTransListError> { - let mut outputs = Vec::new(); - - // We do not change the original MutualCredit. - // Instead, we are operating over a clone: - // This operation is not very expensive, because we are using immutable data structures - // (specifically, HashMaps). - - for (index, friend_tc_op) in operations.into_iter().enumerate() { - match process_operation(mutual_credit, friend_tc_op, remote_max_debt) { - Err(e) => { - return Err(ProcessTransListError { - index, - process_trans_error: e, - }) - } - Ok(trans_output) => outputs.push(trans_output), - } - } - Ok(outputs) +#[derive(Debug, From)] +pub enum ProcessOperationError { + InvalidState, + OpError(OpError), } -pub fn process_operation( - mutual_credit: &mut MutualCredit, - friend_tc_op: FriendTcOp, +pub async fn process_operation( + mc_client: &mut impl McDbClient, + mc_op: McOp, + currency: &Currency, + remote_public_key: &PublicKey, remote_max_debt: u128, -) -> Result { - match friend_tc_op { - FriendTcOp::RequestSendFunds(request_send_funds) => { - process_request_send_funds(mutual_credit, request_send_funds, remote_max_debt) - } - FriendTcOp::ResponseSendFunds(response_send_funds) => { - process_response_send_funds(mutual_credit, response_send_funds) - } - FriendTcOp::CancelSendFunds(cancel_send_funds) => { - process_cancel_send_funds(mutual_credit, cancel_send_funds) +) -> Result { + match mc_op { + McOp::Request(request) => { + process_request_send_funds(mc_client, request, currency, remote_max_debt).await } - FriendTcOp::CollectSendFunds(collect_send_funds) => { - process_collect_send_funds(mutual_credit, collect_send_funds) + McOp::Response(response) => { + process_response_send_funds(mc_client, response, currency, remote_public_key).await } + McOp::Cancel(cancel) => process_cancel_send_funds(mc_client, cancel).await, } } /// Process an incoming RequestSendFundsOp -fn process_request_send_funds( - mutual_credit: &mut MutualCredit, - request_send_funds: RequestSendFundsOp, +async fn process_request_send_funds( + mc_client: &mut impl McDbClient, + request: McRequest, + currency: &Currency, remote_max_debt: u128, -) -> Result { - if !request_send_funds.route.is_part_valid() { - return Err(ProcessOperationError::InvalidRoute); - } - - if request_send_funds.dest_payment > request_send_funds.total_dest_payment { - return Err(ProcessOperationError::DestPaymentExceedsTotal); - } - - /* - // Make sure that we are open to requests: - if !mutual_credit.state().requests_status.local.is_open() { - return Err(ProcessOperationError::LocalRequestsClosed); +) -> Result { + // Make sure that we don't have this request as a pending request already: + if mc_client + .get_remote_pending_transaction(request.request_id.clone()) + .await? + .is_some() + { + // Request already exists + return Ok(ProcessOutput::Inconsistency); } - */ - // Make sure that we don't have this request as a pending request already: - let p_remote_requests = &mutual_credit.state().pending_transactions.remote; - if p_remote_requests.contains_key(&request_send_funds.request_id) { - return Err(ProcessOperationError::RequestAlreadyExists); + if !request.route.is_part_valid() { + // Invalid route. We cancel the request + return Ok(ProcessOutput::Incoming(IncomingMessage::RequestCancel( + request, + ))); } // Calculate amount of credits to freeze - let own_freeze_credits = request_send_funds - .dest_payment - .checked_add(request_send_funds.left_fees) - .ok_or(ProcessOperationError::CreditsCalcOverflow)?; + let own_freeze_credits = + if let Some(own_freeze_credits) = request.dest_payment.checked_add(request.left_fees) { + own_freeze_credits + } else { + // Credits calculation overflow. We cancel the request: + return Ok(ProcessOutput::Incoming(IncomingMessage::RequestCancel( + request, + ))); + }; // Make sure we can freeze the credits - let balance = &mutual_credit.state().balance; + let mc_balance = mc_client.get_balance().await?; - let new_remote_pending_debt = balance + let new_remote_pending_debt = if let Some(new_remote_pending_debt) = mc_balance .remote_pending_debt .checked_add(own_freeze_credits) - .ok_or(ProcessOperationError::CreditsCalcOverflow)?; + { + new_remote_pending_debt + } else { + // Credits calculation overflow. We cancel the request: + return Ok(ProcessOutput::Incoming(IncomingMessage::RequestCancel( + request, + ))); + }; - // Check that local_pending_debt - balance <= local_max_debt: - let add = balance + // Check that balance + remote_pending_debt - remote_max_debt > 0: + let add = if let Some(add) = mc_balance .balance .checked_add_unsigned(new_remote_pending_debt) - .ok_or(ProcessOperationError::CreditsCalcOverflow)?; - - let incoming_message = if add - .checked_sub_unsigned(remote_max_debt) - .ok_or(ProcessOperationError::CreditsCalcOverflow)? - > 0 { - // Insufficient trust: - IncomingMessage::RequestCancel(request_send_funds.clone()) + add } else { - IncomingMessage::Request(request_send_funds.clone()) + // Credits calculation overflow. We cancel the request: + return Ok(ProcessOutput::Incoming(IncomingMessage::RequestCancel( + request, + ))); }; - // Add pending transaction: - let pending_transaction = create_pending_transaction(&request_send_funds); - - // let pending_friend_request = create_pending_transaction(&request_send_funds); - - let mut op_output = ProcessOperationOutput { - incoming_message: Some(incoming_message), - mc_mutations: Vec::new(), + let incoming_message = if add.saturating_sub_unsigned(remote_max_debt) > 0 { + // Insufficient trust: + IncomingMessage::RequestCancel(request.clone()) + } else { + IncomingMessage::Request(request.clone()) }; - let mc_mutation = McMutation::InsertRemotePendingTransaction(pending_transaction); - mutual_credit.mutate(&mc_mutation); - op_output.mc_mutations.push(mc_mutation); + // Add pending transaction: + let pending_transaction = PendingTransaction::from(request.clone()); + mc_client + .insert_remote_pending_transaction(pending_transaction) + .await?; // If we are here, we can freeze the credits: - let mc_mutation = McMutation::SetRemotePendingDebt(new_remote_pending_debt); - mutual_credit.mutate(&mc_mutation); - op_output.mc_mutations.push(mc_mutation); + mc_client + .set_remote_pending_debt(new_remote_pending_debt) + .await?; - Ok(op_output) + Ok(ProcessOutput::Incoming(incoming_message)) } -fn process_response_send_funds( - mutual_credit: &mut MutualCredit, - response_send_funds: ResponseSendFundsOp, -) -> Result { +async fn process_response_send_funds( + mc_client: &mut impl McDbClient, + response: McResponse, + currency: &Currency, + remote_public_key: &PublicKey, +) -> Result { // Make sure that id exists in local_pending hashmap, // and access saved request details. - let local_pending_transactions = &mutual_credit.state().pending_transactions.local; // Obtain pending request: - // TODO: Possibly get rid of clone() here for optimization later - let pending_transaction = local_pending_transactions - .get(&response_send_funds.request_id) - .ok_or(ProcessOperationError::RequestDoesNotExist)? - .clone(); - - let dest_public_key = if pending_transaction.route.public_keys.is_empty() { - &mutual_credit.state().idents.remote_public_key + let pending_transaction = if let Some(pending_transaction) = mc_client + .get_local_pending_transaction(response.request_id.clone()) + .await? + { + pending_transaction + } else { + // Request does not exist + return Ok(ProcessOutput::Inconsistency); + }; + + // Verify src_plain_lock and dest_plain_lock: + if response.src_plain_lock.hash_lock() != pending_transaction.src_hashed_lock { + // Invalid src plain lock + return Ok(ProcessOutput::Inconsistency); + } + + let dest_public_key = if pending_transaction.route.is_empty() { + remote_public_key } else { - pending_transaction.route.public_keys.last().unwrap() + pending_transaction + .route + .last() + .ok_or(ProcessOperationError::InvalidState)? }; - let response_signature_buffer = create_response_signature_buffer( - &mutual_credit.state().currency, - response_send_funds.clone(), - &pending_transaction, - ); + let response_signature_buffer = + mc_response_signature_buffer(¤cy, &response, &pending_transaction); // Verify response funds signature: if !verify_signature( &response_signature_buffer, dest_public_key, - &response_send_funds.signature, + &response.signature, ) { - return Err(ProcessOperationError::InvalidResponseSignature); - } - - // We expect that the current stage is Request: - if let TransactionStage::Request = pending_transaction.stage { - } else { - return Err(ProcessOperationError::NotExpectingResponse); + // Invalid response signature + return Ok(ProcessOutput::Inconsistency); } - let mut mc_mutations = Vec::new(); - - // Set the stage to Response, and remember dest_hashed_lock: - let mc_mutation = McMutation::SetLocalPendingTransactionStage(( - response_send_funds.request_id.clone(), - TransactionStage::Response( - response_send_funds.dest_hashed_lock.clone(), - response_send_funds.is_complete, - ), - )); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - let incoming_message = Some(IncomingMessage::Response(IncomingResponseSendFundsOp { - pending_transaction, - incoming_response: response_send_funds, - })); - - Ok(ProcessOperationOutput { - incoming_message, - mc_mutations, - }) -} - -fn process_cancel_send_funds( - mutual_credit: &mut MutualCredit, - cancel_send_funds: CancelSendFundsOp, -) -> Result { - // Make sure that id exists in local_pending hashmap, - // and access saved request details. - let local_pending_transactions = &mutual_credit.state().pending_transactions.local; - - // Obtain pending request: - let pending_transaction = local_pending_transactions - .get(&cancel_send_funds.request_id) - .ok_or(ProcessOperationError::RequestDoesNotExist)? - .clone(); - // TODO: Possibly get rid of clone() here for optimization later - - let mut mc_mutations = Vec::new(); - - // Remove entry from local_pending hashmap: - let mc_mutation = - McMutation::RemoveLocalPendingTransaction(cancel_send_funds.request_id.clone()); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - + // Calculate amount of credits that were frozen: let freeze_credits = pending_transaction .dest_payment .checked_add(pending_transaction.left_fees) - .unwrap(); + .ok_or(ProcessOperationError::InvalidState)?; + // Note: The above should never fail, because this was already checked during the + // request message processing. + + // Remove entry from local_pending hashmap: + mc_client + .remove_local_pending_transaction(response.request_id.clone()) + .await?; // Decrease frozen credits: - let new_local_pending_debt = mutual_credit - .state() - .balance + let mc_balance = mc_client.get_balance().await?; + let new_local_pending_debt = mc_balance .local_pending_debt .checked_sub(freeze_credits) - .unwrap(); - - let mc_mutation = McMutation::SetLocalPendingDebt(new_local_pending_debt); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - // Return cancel_send_funds: - let incoming_message = Some(IncomingMessage::Cancel(IncomingCancelSendFundsOp { - pending_transaction, - incoming_cancel: cancel_send_funds, - })); - - Ok(ProcessOperationOutput { - incoming_message, - mc_mutations, - }) + .ok_or(ProcessOperationError::InvalidState)?; + mc_client + .set_local_pending_debt(new_local_pending_debt) + .await?; + + // Update out_fees: + mc_client + .set_out_fees( + mc_balance + .out_fees + .checked_add(pending_transaction.left_fees.into()) + .ok_or(ProcessOperationError::InvalidState)?, + ) + .await?; + + // Decrease balance: + let mc_balance = mc_client.get_balance().await?; + let new_balance = mc_balance + .balance + .checked_sub_unsigned(freeze_credits) + .ok_or(ProcessOperationError::InvalidState)?; + mc_client.set_balance(new_balance).await?; + + Ok(ProcessOutput::Incoming(IncomingMessage::Response( + IncomingResponseSendFundsOp { + pending_transaction, + incoming_response: response, + }, + ))) } -fn process_collect_send_funds( - mutual_credit: &mut MutualCredit, - collect_send_funds: CollectSendFundsOp, -) -> Result { +async fn process_cancel_send_funds( + mc_client: &mut impl McDbClient, + cancel: McCancel, +) -> Result { // Make sure that id exists in local_pending hashmap, // and access saved request details. - let local_pending_transactions = &mutual_credit.state().pending_transactions.local; // Obtain pending request: - // TODO: Possibly get rid of clone() here for optimization later - let pending_transaction = local_pending_transactions - .get(&collect_send_funds.request_id) - .ok_or(ProcessOperationError::RequestDoesNotExist)? - .clone(); - - let dest_hashed_lock = match &pending_transaction.stage { - TransactionStage::Response(dest_hashed_lock, _is_complete) => dest_hashed_lock, - _ => return Err(ProcessOperationError::NotExpectingCollect), + let pending_transaction = if let Some(pending_transaction) = mc_client + .get_local_pending_transaction(cancel.request_id.clone()) + .await? + { + pending_transaction + } else { + // Request does not exist + return Ok(ProcessOutput::Inconsistency); }; - // Verify src_plain_lock and dest_plain_lock: - if collect_send_funds.src_plain_lock.hash_lock() != pending_transaction.src_hashed_lock { - return Err(ProcessOperationError::InvalidSrcPlainLock); - } - - if collect_send_funds.dest_plain_lock.hash_lock() != *dest_hashed_lock { - return Err(ProcessOperationError::InvalidDestPlainLock); - } + mc_client + .remove_local_pending_transaction(cancel.request_id.clone()) + .await?; - // Calculate amount of credits that were frozen: let freeze_credits = pending_transaction .dest_payment .checked_add(pending_transaction.left_fees) - .unwrap(); - // Note: The unwrap() above should never fail, because this was already checked during the - // request message processing. + .ok_or(ProcessOperationError::InvalidState)?; - let mut mc_mutations = Vec::new(); - - // Remove entry from local_pending hashmap: - let mc_mutation = - McMutation::RemoveLocalPendingTransaction(collect_send_funds.request_id.clone()); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - // Decrease frozen credits and decrease balance: - let new_local_pending_debt = mutual_credit - .state() - .balance + let mc_balance = mc_client.get_balance().await?; + // Decrease frozen credits: + let new_local_pending_debt = mc_balance .local_pending_debt .checked_sub(freeze_credits) - .unwrap(); + .ok_or(ProcessOperationError::InvalidState)?; - let mc_mutation = McMutation::SetLocalPendingDebt(new_local_pending_debt); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); + mc_client + .set_local_pending_debt(new_local_pending_debt) + .await?; - let new_balance = mutual_credit - .state() - .balance - .balance - .checked_sub_unsigned(freeze_credits) - .unwrap(); - - let mc_mutation = McMutation::SetBalance(new_balance); - mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - let incoming_message = Some(IncomingMessage::Collect(IncomingCollectSendFundsOp { - pending_transaction, - incoming_collect: collect_send_funds, - })); - - Ok(ProcessOperationOutput { - incoming_message, - mc_mutations, - }) + // Return cancel_send_funds: + Ok(ProcessOutput::Incoming(IncomingMessage::Cancel( + IncomingCancelSendFundsOp { + pending_transaction, + incoming_cancel: cancel, + }, + ))) } diff --git a/components/funder/src/mutual_credit/mod.rs b/components/funder/src/mutual_credit/mod.rs index a3521f69d..78392a01e 100644 --- a/components/funder/src/mutual_credit/mod.rs +++ b/components/funder/src/mutual_credit/mod.rs @@ -1,5 +1,13 @@ +pub mod utils; + pub mod incoming; pub mod outgoing; #[cfg(test)] -mod tests; -pub mod types; +pub mod tests; + +mod types; + +pub use types::{McCancel, McDbClient, McOp, McRequest, McResponse, PendingTransaction}; + +#[cfg(test)] +pub use tests::MockMutualCredit; diff --git a/components/funder/src/mutual_credit/outgoing.rs b/components/funder/src/mutual_credit/outgoing.rs index 403c121ad..c817eb521 100644 --- a/components/funder/src/mutual_credit/outgoing.rs +++ b/components/funder/src/mutual_credit/outgoing.rs @@ -1,297 +1,260 @@ +use derive_more::From; + use crypto::hash_lock::HashLock; use crypto::identity::verify_signature; +use common::async_rpc::OpError; use common::safe_arithmetic::SafeSignedArithmetic; -use proto::funder::messages::{ - CancelSendFundsOp, CollectSendFundsOp, FriendTcOp, RequestSendFundsOp, ResponseSendFundsOp, - TransactionStage, -}; +use proto::crypto::PublicKey; +use proto::funder::messages::Currency; use signature::signature_buff::create_response_signature_buffer; -use crate::types::create_pending_transaction; - -use super::types::{McMutation, MutualCredit}; +use crate::route::Route; -/// Processes outgoing funds for a token channel. -/// Used to batch as many funds as possible. -#[derive(Debug)] -pub struct OutgoingMc { - mutual_credit: MutualCredit, -} +use crate::mutual_credit::types::{ + McCancel, McDbClient, McOp, McRequest, McResponse, PendingTransaction, +}; +use crate::mutual_credit::utils::{mc_response_signature_buffer, response_op_from_mc_response}; -#[derive(Debug)] +#[derive(Debug, From)] pub enum QueueOperationError { - RemoteMaxDebtTooLarge, InvalidRoute, - PkPairNotInRoute, - CreditsCalcOverflow, RequestAlreadyExists, RequestDoesNotExist, InvalidResponseSignature, - NotExpectingResponse, - NotExpectingCollect, InvalidSrcPlainLock, - InvalidDestPlainLock, - DestPaymentExceedsTotal, + InvalidState, + CreditsCalcOverflow, + OpError(OpError), } -/// A wrapper over a token channel, accumulating operations to be sent as one transaction. -impl OutgoingMc { - // TODO: Take MutualCredit instead of &MutualCredit? - pub fn new(mutual_credit: &MutualCredit) -> OutgoingMc { - OutgoingMc { - mutual_credit: mutual_credit.clone(), +/* +/// Queue a single operation mutual credit operation +pub async fn queue_operation( + mc_client: &mut impl McDbClient, + operation: McOp, + currency: &Currency, + local_public_key: &PublicKey, +) -> Result<(), QueueOperationError> { + match operation { + McOp::Request(request) => queue_request(mc_client, request, currency).await, + McOp::Response(response) => { + queue_response(mc_client, response, currency, local_public_key).await } + McOp::Cancel(cancel) => queue_cancel(mc_client, cancel).await, } - - pub fn queue_operation( - &mut self, - operation: &FriendTcOp, - ) -> Result, QueueOperationError> { - // TODO: Maybe remove clone from here later: - match operation.clone() { - FriendTcOp::RequestSendFunds(request_send_funds) => { - self.queue_request_send_funds(request_send_funds) - } - FriendTcOp::ResponseSendFunds(response_send_funds) => { - self.queue_response_send_funds(response_send_funds) - } - FriendTcOp::CancelSendFunds(cancel_send_funds) => { - self.queue_cancel_send_funds(cancel_send_funds) - } - FriendTcOp::CollectSendFunds(collect_send_funds) => { - self.queue_collect_send_funds(collect_send_funds) - } - } - } - - fn queue_request_send_funds( - &mut self, - request_send_funds: RequestSendFundsOp, - ) -> Result, QueueOperationError> { - if !request_send_funds.route.is_part_valid() { - return Err(QueueOperationError::InvalidRoute); - } - - if request_send_funds.dest_payment > request_send_funds.total_dest_payment { - return Err(QueueOperationError::DestPaymentExceedsTotal); - } - - /* - // Make sure that remote side is open to requests: - if !self.mutual_credit.state().requests_status.remote.is_open() { - return Err(QueueOperationError::RemoteRequestsClosed); - } - */ - - // Calculate amount of credits to freeze - let own_freeze_credits = request_send_funds - .dest_payment - .checked_add(request_send_funds.left_fees) - .ok_or(QueueOperationError::CreditsCalcOverflow)?; - - let balance = &self.mutual_credit.state().balance; - - // Make sure we can freeze the credits - let new_local_pending_debt = balance - .local_pending_debt - .checked_add(own_freeze_credits) - .ok_or(QueueOperationError::CreditsCalcOverflow)?; - - let p_local_requests = &self.mutual_credit.state().pending_transactions.local; - - // Make sure that we don't have this request as a pending request already: - if p_local_requests.contains_key(&request_send_funds.request_id) { - return Err(QueueOperationError::RequestAlreadyExists); - } - - // Add pending transaction: - let pending_transaction = create_pending_transaction(&request_send_funds); - - let mut mc_mutations = Vec::new(); - let mc_mutation = McMutation::InsertLocalPendingTransaction(pending_transaction); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - // If we are here, we can freeze the credits: - let mc_mutation = McMutation::SetLocalPendingDebt(new_local_pending_debt); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - Ok(mc_mutations) +} +*/ + +pub async fn queue_request( + mc_client: &mut impl McDbClient, + request: McRequest, + currency: &Currency, + local_max_debt: u128, +) -> Result, QueueOperationError> { + if !request.route.is_part_valid() { + return Err(QueueOperationError::InvalidRoute); } - fn queue_response_send_funds( - &mut self, - response_send_funds: ResponseSendFundsOp, - ) -> Result, QueueOperationError> { - // Make sure that id exists in remote_pending hashmap, - // and access saved request details. - let remote_pending_transactions = &self.mutual_credit.state().pending_transactions.remote; - - // Obtain pending request: - let pending_transaction = remote_pending_transactions - .get(&response_send_funds.request_id) - .ok_or(QueueOperationError::RequestDoesNotExist)? - .clone(); - // TODO: Possibly get rid of clone() here for optimization later - - // verify signature: - let response_signature_buffer = create_response_signature_buffer( - &self.mutual_credit.state().currency, - response_send_funds.clone(), - &pending_transaction, - ); - // The response was signed by the destination node: - let dest_public_key = if pending_transaction.route.public_keys.is_empty() { - &self.mutual_credit.state().idents.local_public_key + // Calculate amount of credits to freeze + let own_freeze_credits = + if let Some(own_freeze_credits) = request.dest_payment.checked_add(request.left_fees) { + own_freeze_credits } else { - pending_transaction.route.public_keys.last().unwrap() + // Freeze calculation overflow: + return Ok(Err(McCancel { + request_id: request.request_id, + })); }; - // Verify response funds signature: - if !verify_signature( - &response_signature_buffer, - dest_public_key, - &response_send_funds.signature, - ) { - return Err(QueueOperationError::InvalidResponseSignature); - } + let mc_balance = mc_client.get_balance().await?; + + // Make sure we can freeze the credits + let new_local_pending_debt = if let Some(new_local_pending_debt) = mc_balance + .local_pending_debt + .checked_add(own_freeze_credits) + { + new_local_pending_debt + } else { + // Freeze calculation overflow: + return Ok(Err(McCancel { + request_id: request.request_id, + })); + }; + + // Make sure that we don't get into too much debt: + // Check that balance - local_pending_debt + local_max_debt >= 0 + // balance - local_pending_debt >= - local_max_debt + let sub = if let Some(sub) = mc_balance + .balance + .checked_sub_unsigned(new_local_pending_debt) + { + sub + } else { + // balance - local_pending_debt underflow: + return Ok(Err(McCancel { + request_id: request.request_id, + })); + }; + + if sub.saturating_add_unsigned(local_max_debt) < 0 { + // Too much local debt: + return Ok(Err(McCancel { + request_id: request.request_id, + })); + } - // We expect that the current stage is Request: - if let TransactionStage::Request = pending_transaction.stage { - } else { - return Err(QueueOperationError::NotExpectingResponse); - } + // Make sure that we don't have this request as a pending request already: + if mc_client + .get_local_pending_transaction(request.request_id.clone()) + .await? + .is_some() + { + // Request already exists: + return Ok(Err(McCancel { + request_id: request.request_id, + })); + } - let mut mc_mutations = Vec::new(); + // Add pending transaction: + let pending_transaction = PendingTransaction::from(request.clone()); + mc_client + .insert_local_pending_transaction(pending_transaction) + .await?; - // Set the stage to Response, and remember dest_hashed_lock: - let mc_mutation = McMutation::SetRemotePendingTransactionStage(( - response_send_funds.request_id, - TransactionStage::Response( - response_send_funds.dest_hashed_lock.clone(), - response_send_funds.is_complete, - ), - )); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); + // If we are here, we can freeze the credits: + mc_client + .set_local_pending_debt(new_local_pending_debt) + .await?; - Ok(mc_mutations) - } + Ok(Ok(())) +} - fn queue_cancel_send_funds( - &mut self, - cancel_send_funds: CancelSendFundsOp, - ) -> Result, QueueOperationError> { - // Make sure that id exists in remote_pending hashmap, - // and access saved request details. - let remote_pending_transactions = &self.mutual_credit.state().pending_transactions.remote; - - // Obtain pending request: - let pending_transaction = remote_pending_transactions - .get(&cancel_send_funds.request_id) - .ok_or(QueueOperationError::RequestDoesNotExist)?; - - let freeze_credits = pending_transaction - .dest_payment - .checked_add(pending_transaction.left_fees) - .unwrap(); - - // Remove entry from remote hashmap: - let mut mc_mutations = Vec::new(); - - let mc_mutation = McMutation::RemoveRemotePendingTransaction(cancel_send_funds.request_id); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - // Decrease frozen credits: - let new_remote_pending_debt = self - .mutual_credit - .state() - .balance - .remote_pending_debt - .checked_sub(freeze_credits) - .unwrap(); - - let mc_mutation = McMutation::SetRemotePendingDebt(new_remote_pending_debt); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - Ok(mc_mutations) +pub async fn queue_response( + mc_client: &mut impl McDbClient, + response: McResponse, + currency: &Currency, + local_public_key: &PublicKey, +) -> Result<(), QueueOperationError> { + // Make sure that id exists in remote_pending hashmap, + // and access saved request details. + + // Obtain pending request: + let pending_transaction = mc_client + .get_remote_pending_transaction(response.request_id.clone()) + .await? + .ok_or(QueueOperationError::RequestDoesNotExist)?; + // TODO: Possibly get rid of clone() here for optimization later + + // Verify src_plain_lock and dest_plain_lock: + if response.src_plain_lock.hash_lock() != pending_transaction.src_hashed_lock { + return Err(QueueOperationError::InvalidSrcPlainLock); } - fn queue_collect_send_funds( - &mut self, - collect_send_funds: CollectSendFundsOp, - ) -> Result, QueueOperationError> { - // Make sure that id exists in remote_pending hashmap, - // and access saved request details. - let remote_pending_transactions = &self.mutual_credit.state().pending_transactions.remote; - - // Obtain pending request: - let pending_transaction = remote_pending_transactions - .get(&collect_send_funds.request_id) - .ok_or(QueueOperationError::RequestDoesNotExist)? - .clone(); - // TODO: Possibly get rid of clone() here for optimization later - - let dest_hashed_lock = match &pending_transaction.stage { - TransactionStage::Response(dest_hashed_lock, _is_complete) => dest_hashed_lock, - _ => return Err(QueueOperationError::NotExpectingCollect), - }; - - // Verify src_plain_lock and dest_plain_lock: - if collect_send_funds.src_plain_lock.hash_lock() != pending_transaction.src_hashed_lock { - return Err(QueueOperationError::InvalidSrcPlainLock); - } + // verify signature: + let response_signature_buffer = + mc_response_signature_buffer(¤cy, &response, &pending_transaction); + + // The response was signed by the destination node: + let dest_public_key = if pending_transaction.route.is_empty() { + local_public_key + } else { + pending_transaction + .route + .last() + .ok_or(QueueOperationError::InvalidState)? + }; + + // Verify response funds signature: + if !verify_signature( + &response_signature_buffer, + dest_public_key, + &response.signature, + ) { + return Err(QueueOperationError::InvalidResponseSignature); + } - if collect_send_funds.dest_plain_lock.hash_lock() != *dest_hashed_lock { - return Err(QueueOperationError::InvalidDestPlainLock); - } + // Calculate amount of credits that were frozen: + let freeze_credits = pending_transaction + .dest_payment + .checked_add(pending_transaction.left_fees) + .ok_or(QueueOperationError::CreditsCalcOverflow)?; + + // Remove entry from remote_pending hashmap: + mc_client + .remove_remote_pending_transaction(response.request_id) + .await?; + + // Decrease frozen credits + let mc_balance = mc_client.get_balance().await?; + let new_remote_pending_debt = mc_balance + .remote_pending_debt + .checked_sub(freeze_credits) + .ok_or(QueueOperationError::CreditsCalcOverflow)?; + // The above should never fail, as it was already checked when a request message was received. + + mc_client + .set_remote_pending_debt(new_remote_pending_debt) + .await?; + + // Update in_fees: + mc_client + .set_in_fees( + mc_balance + .in_fees + .checked_add(pending_transaction.left_fees.into()) + .ok_or(QueueOperationError::CreditsCalcOverflow)?, + ) + .await?; + + // Increase balance: + let mc_balance = mc_client.get_balance().await?; + let new_balance = mc_balance + .balance + .checked_add_unsigned(freeze_credits) + .ok_or(QueueOperationError::CreditsCalcOverflow)?; + // The above should never fail. This was already checked when a request message was + // received. + + mc_client.set_balance(new_balance).await?; + + Ok(()) +} - // Calculate amount of credits that were frozen: - let freeze_credits = pending_transaction - .dest_payment - .checked_add(pending_transaction.left_fees) - .unwrap(); - - // Remove entry from remote_pending hashmap: - let mut mc_mutations = Vec::new(); - let mc_mutation = McMutation::RemoveRemotePendingTransaction(collect_send_funds.request_id); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - // Decrease frozen credits and increase balance: - let new_remote_pending_debt = self - .mutual_credit - .state() - .balance - .remote_pending_debt - .checked_sub(freeze_credits) - .unwrap(); - // Above unwrap() should never fail. This was already checked when a request message was - // received. - - let mc_mutation = McMutation::SetRemotePendingDebt(new_remote_pending_debt); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - let new_balance = self - .mutual_credit - .state() - .balance - .balance - .checked_add_unsigned(freeze_credits) - .unwrap(); - // Above unwrap() should never fail. This was already checked when a request message was - // received. - - let mc_mutation = McMutation::SetBalance(new_balance); - self.mutual_credit.mutate(&mc_mutation); - mc_mutations.push(mc_mutation); - - Ok(mc_mutations) - } +pub async fn queue_cancel( + mc_client: &mut impl McDbClient, + cancel: McCancel, +) -> Result<(), QueueOperationError> { + // Make sure that id exists in remote_pending hashmap, + // and access saved request details. + + // Obtain pending request: + let pending_transaction = mc_client + .get_remote_pending_transaction(cancel.request_id.clone()) + .await? + .ok_or(QueueOperationError::RequestDoesNotExist)?; + + let freeze_credits = pending_transaction + .dest_payment + .checked_add(pending_transaction.left_fees) + .ok_or(QueueOperationError::CreditsCalcOverflow)?; + + // Remove entry from remote hashmap: + mc_client + .remove_remote_pending_transaction(cancel.request_id.clone()) + .await?; + + // Decrease frozen credits: + let mc_balance = mc_client.get_balance().await?; + let new_remote_pending_debt = mc_balance + .remote_pending_debt + .checked_sub(freeze_credits) + .ok_or(QueueOperationError::CreditsCalcOverflow)?; + + mc_client + .set_remote_pending_debt(new_remote_pending_debt) + .await?; + + Ok(()) } diff --git a/components/funder/src/mutual_credit/tests.rs b/components/funder/src/mutual_credit/tests.rs deleted file mode 100644 index 580dbe7dd..000000000 --- a/components/funder/src/mutual_credit/tests.rs +++ /dev/null @@ -1,307 +0,0 @@ -use std::convert::TryFrom; - -use crypto::hash_lock::HashLock; -use crypto::identity::{Identity, SoftwareEd25519Identity}; -use crypto::rand::RandGen; -use crypto::test_utils::DummyRandom; - -use proto::crypto::{InvoiceId, PlainLock, PrivateKey, PublicKey, RandValue, Signature, Uid}; -use proto::funder::messages::{ - CancelSendFundsOp, CollectSendFundsOp, Currency, FriendTcOp, FriendsRoute, RequestSendFundsOp, - ResponseSendFundsOp, -}; -use signature::signature_buff::create_response_signature_buffer; - -use crate::types::create_pending_transaction; - -use crate::mutual_credit::incoming::{ - process_operation, ProcessOperationError, ProcessOperationOutput, -}; -use crate::mutual_credit::outgoing::{OutgoingMc, QueueOperationError}; -use crate::mutual_credit::types::MutualCredit; - -/// Helper function for applying an outgoing operation over a token channel. -fn apply_outgoing( - mutual_credit: &mut MutualCredit, - friend_tc_op: &FriendTcOp, -) -> Result<(), QueueOperationError> { - let mut outgoing = OutgoingMc::new(mutual_credit); - let mutations = outgoing.queue_operation(friend_tc_op)?; - - for mutation in mutations { - mutual_credit.mutate(&mutation); - } - Ok(()) -} - -/// Helper function for applying an incoming operation over a token channel. -fn apply_incoming( - mut mutual_credit: &mut MutualCredit, - friend_tc_op: FriendTcOp, - remote_max_debt: u128, -) -> Result { - process_operation(&mut mutual_credit, friend_tc_op, remote_max_debt) -} - -#[test] -fn test_request_response_collect_send_funds() { - let currency = Currency::try_from("OFFSET".to_owned()).unwrap(); - - let local_public_key = PublicKey::from(&[0xaa; PublicKey::len()]); - let remote_public_key = PublicKey::from(&[0xbb; PublicKey::len()]); - let balance = 0; - let mut mutual_credit = - MutualCredit::new(&local_public_key, &remote_public_key, ¤cy, balance); - - // -----[RequestSendFunds]-------- - // ----------------------------- - let mut rng = DummyRandom::new(&[1u8]); - let private_key = PrivateKey::rand_gen(&mut rng); - let identity = SoftwareEd25519Identity::from_private_key(&private_key).unwrap(); - let public_key_c = identity.get_public_key(); - - let request_id = Uid::from(&[3; Uid::len()]); - let route = FriendsRoute { - public_keys: vec![ - PublicKey::from(&[0xaa; PublicKey::len()]), - PublicKey::from(&[0xbb; PublicKey::len()]), - public_key_c.clone(), - ], - }; - let invoice_id = InvoiceId::from(&[0; InvoiceId::len()]); - let src_plain_lock = PlainLock::from(&[1; PlainLock::len()]); - - let request_send_funds = RequestSendFundsOp { - request_id: request_id.clone(), - src_hashed_lock: src_plain_lock.hash_lock(), - route, - dest_payment: 10, - total_dest_payment: 10, - invoice_id, - left_fees: 5, - }; - - let pending_transaction = create_pending_transaction(&request_send_funds); - apply_outgoing( - &mut mutual_credit, - &FriendTcOp::RequestSendFunds(request_send_funds), - ) - .unwrap(); - - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 10 + 5); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); - - // -----[ResponseSendFunds]-------- - // -------------------------------- - let rand_nonce = RandValue::from(&[5; RandValue::len()]); - let dest_plain_lock = PlainLock::from(&[2; PlainLock::len()]); - - let mut response_send_funds = ResponseSendFundsOp { - request_id: request_id.clone(), - dest_hashed_lock: dest_plain_lock.hash_lock(), - is_complete: false, - rand_nonce: rand_nonce.clone(), - signature: Signature::from(&[0; Signature::len()]), - }; - - let sign_buffer = create_response_signature_buffer( - ¤cy, - response_send_funds.clone(), - &pending_transaction, - ); - response_send_funds.signature = identity.sign(&sign_buffer); - - apply_incoming( - &mut mutual_credit, - FriendTcOp::ResponseSendFunds(response_send_funds), - 100, - ) - .unwrap(); - - // We expect that no changes to balance happened yet: - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 10 + 5); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); - - // -----[CollectSendFunds]-------- - // -------------------------------- - let collect_send_funds = CollectSendFundsOp { - request_id, - src_plain_lock, - dest_plain_lock, - }; - - apply_incoming( - &mut mutual_credit, - FriendTcOp::CollectSendFunds(collect_send_funds), - 100, - ) - .unwrap(); - - // We expect that no changes to balance happened yet: - assert_eq!(mutual_credit.state().balance.balance, -15); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 0); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); -} - -#[test] -fn test_request_cancel_send_funds() { - let currency = Currency::try_from("OFFSET".to_owned()).unwrap(); - - let mut rng = DummyRandom::new(&[1u8]); - let private_key = PrivateKey::rand_gen(&mut rng); - let identity = SoftwareEd25519Identity::from_private_key(&private_key).unwrap(); - let public_key_b = identity.get_public_key(); - - let local_public_key = PublicKey::from(&[0xaa; PublicKey::len()]); - let remote_public_key = public_key_b.clone(); - let balance = 0; - let mut mutual_credit = - MutualCredit::new(&local_public_key, &remote_public_key, ¤cy, balance); - - // -----[RequestSendFunds]-------- - // ----------------------------- - let request_id = Uid::from(&[3; Uid::len()]); - let route = FriendsRoute { - public_keys: vec![ - PublicKey::from(&[0xaa; PublicKey::len()]), - public_key_b.clone(), - PublicKey::from(&[0xcc; PublicKey::len()]), - ], - }; - let invoice_id = InvoiceId::from(&[0; InvoiceId::len()]); - let src_plain_lock = PlainLock::from(&[1; PlainLock::len()]); - - let request_send_funds = RequestSendFundsOp { - request_id: request_id.clone(), - src_hashed_lock: src_plain_lock.hash_lock(), - route, - dest_payment: 10, - total_dest_payment: 10, - invoice_id, - left_fees: 5, - }; - - apply_outgoing( - &mut mutual_credit, - &FriendTcOp::RequestSendFunds(request_send_funds), - ) - .unwrap(); - - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 10 + 5); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); - - // -----[CancelSendFunds]-------- - // ------------------------------ - let cancel_send_funds = CancelSendFundsOp { request_id }; - - apply_incoming( - &mut mutual_credit, - FriendTcOp::CancelSendFunds(cancel_send_funds), - 100, - ) - .unwrap(); - - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 0); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); -} - -#[test] -fn test_request_response_cancel_send_funds() { - let currency = Currency::try_from("OFFSET".to_owned()).unwrap(); - - let local_public_key = PublicKey::from(&[0xaa; PublicKey::len()]); - let remote_public_key = PublicKey::from(&[0xbb; PublicKey::len()]); - let balance = 0; - let mut mutual_credit = - MutualCredit::new(&local_public_key, &remote_public_key, ¤cy, balance); - - // -----[RequestSendFunds]-------- - // ----------------------------- - let mut rng = DummyRandom::new(&[1u8]); - let private_key = PrivateKey::rand_gen(&mut rng); - let identity = SoftwareEd25519Identity::from_private_key(&private_key).unwrap(); - let public_key_c = identity.get_public_key(); - - let request_id = Uid::from(&[3; Uid::len()]); - let route = FriendsRoute { - public_keys: vec![ - PublicKey::from(&[0xaa; PublicKey::len()]), - PublicKey::from(&[0xbb; PublicKey::len()]), - public_key_c.clone(), - ], - }; - let invoice_id = InvoiceId::from(&[0; InvoiceId::len()]); - let src_plain_lock = PlainLock::from(&[1; PlainLock::len()]); - - let request_send_funds = RequestSendFundsOp { - request_id: request_id.clone(), - src_hashed_lock: src_plain_lock.hash_lock(), - route, - dest_payment: 10, - total_dest_payment: 10, - invoice_id, - left_fees: 5, - }; - - let pending_transaction = create_pending_transaction(&request_send_funds); - apply_outgoing( - &mut mutual_credit, - &FriendTcOp::RequestSendFunds(request_send_funds), - ) - .unwrap(); - - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 10 + 5); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); - - // -----[ResponseSendFunds]-------- - // -------------------------------- - let rand_nonce = RandValue::from(&[5; RandValue::len()]); - let dest_plain_lock = PlainLock::from(&[2; PlainLock::len()]); - - let mut response_send_funds = ResponseSendFundsOp { - request_id: request_id.clone(), - dest_hashed_lock: dest_plain_lock.hash_lock(), - is_complete: true, - rand_nonce: rand_nonce.clone(), - signature: Signature::from(&[0; Signature::len()]), - }; - - let sign_buffer = create_response_signature_buffer( - ¤cy, - response_send_funds.clone(), - &pending_transaction, - ); - response_send_funds.signature = identity.sign(&sign_buffer); - - apply_incoming( - &mut mutual_credit, - FriendTcOp::ResponseSendFunds(response_send_funds), - 100, - ) - .unwrap(); - - // We expect that no changes to balance happened yet: - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 10 + 5); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); - - // -----[CancelSendFunds]-------- - // ------------------------------ - let cancel_send_funds = CancelSendFundsOp { request_id }; - - apply_incoming( - &mut mutual_credit, - FriendTcOp::CancelSendFunds(cancel_send_funds), - 100, - ) - .unwrap(); - - assert_eq!(mutual_credit.state().balance.balance, 0); - assert_eq!(mutual_credit.state().balance.local_pending_debt, 0); - assert_eq!(mutual_credit.state().balance.remote_pending_debt, 0); -} diff --git a/components/funder/src/mutual_credit/tests/mod.rs b/components/funder/src/mutual_credit/tests/mod.rs new file mode 100644 index 000000000..2a56d06e2 --- /dev/null +++ b/components/funder/src/mutual_credit/tests/mod.rs @@ -0,0 +1,5 @@ +mod request_cancel; +mod request_response; +mod utils; + +pub use utils::MockMutualCredit; diff --git a/components/funder/src/mutual_credit/tests/request_cancel.rs b/components/funder/src/mutual_credit/tests/request_cancel.rs new file mode 100644 index 000000000..f36090cb2 --- /dev/null +++ b/components/funder/src/mutual_credit/tests/request_cancel.rs @@ -0,0 +1,94 @@ +use std::convert::TryFrom; + +use common::test_executor::TestExecutor; + +use crypto::hash_lock::HashLock; +use crypto::identity::{Identity, SoftwareEd25519Identity}; +use crypto::rand::RandGen; +use crypto::test_utils::DummyRandom; + +use proto::crypto::{HashResult, PlainLock, PrivateKey, PublicKey, Uid}; +use proto::funder::messages::Currency; + +use crate::mutual_credit::outgoing::queue_request; +use crate::mutual_credit::tests::utils::{ + process_operations_list, MockMutualCredit, ProcessListOutput, +}; +use crate::mutual_credit::types::{McCancel, McDbClient, McOp, McRequest}; + +async fn task_request_cancel() { + let currency = Currency::try_from("FST".to_owned()).unwrap(); + + let mut rng = DummyRandom::new(&[1u8]); + let private_key = PrivateKey::rand_gen(&mut rng); + let identity = SoftwareEd25519Identity::from_private_key(&private_key).unwrap(); + let public_key_b = identity.get_public_key(); + + let local_public_key = PublicKey::from(&[0xaa; PublicKey::len()]); + let remote_public_key = public_key_b.clone(); + let balance = 0; + let in_fees = 0.into(); + let out_fees = 0.into(); + let mut mc_transaction = MockMutualCredit::new(currency.clone(), balance, in_fees, out_fees); + + // -----[McRequest]-------- + // ----------------------------- + let request_id = Uid::from(&[3; Uid::len()]); + let route = vec![ + PublicKey::from(&[0xaa; PublicKey::len()]), + public_key_b.clone(), + PublicKey::from(&[0xcc; PublicKey::len()]), + ]; + let invoice_hash = HashResult::from(&[0; HashResult::len()]); + let src_plain_lock = PlainLock::from(&[1; PlainLock::len()]); + + let request = McRequest { + request_id: request_id.clone(), + src_hashed_lock: src_plain_lock.hash_lock(), + route, + dest_payment: 10, + invoice_hash, + left_fees: 5, + }; + + let local_max_debt = u128::MAX; + queue_request(&mut mc_transaction, request, ¤cy, local_max_debt) + .await + .unwrap(); + + let mc_balance = mc_transaction.get_balance().await.unwrap(); + assert_eq!(mc_balance.balance, 0); + assert_eq!(mc_balance.local_pending_debt, 10 + 5); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 0.into()); + + // -----[McCancel]-------- + // ------------------------------ + let cancel = McCancel { request_id }; + + let list_output = process_operations_list( + &mut mc_transaction, + vec![McOp::Cancel(cancel)], + ¤cy, + &remote_public_key, + 100, + ) + .await + .unwrap(); + assert!(matches!(list_output, ProcessListOutput::IncomingList(..))); + + let mc_balance = mc_transaction.get_balance().await.unwrap(); + assert_eq!(mc_balance.balance, 0); + assert_eq!(mc_balance.local_pending_debt, 0); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 0.into()); +} + +#[test] +fn test_request_cancel() { + let test_executor = TestExecutor::new(); + let res = test_executor.run(task_request_cancel()); + assert!(res.is_output()); +} diff --git a/components/funder/src/mutual_credit/tests/request_response.rs b/components/funder/src/mutual_credit/tests/request_response.rs new file mode 100644 index 000000000..7051742ff --- /dev/null +++ b/components/funder/src/mutual_credit/tests/request_response.rs @@ -0,0 +1,109 @@ +use std::convert::TryFrom; + +use common::test_executor::TestExecutor; + +use crypto::hash_lock::HashLock; +use crypto::identity::{Identity, SoftwareEd25519Identity}; +use crypto::rand::RandGen; +use crypto::test_utils::DummyRandom; + +use proto::crypto::{HashResult, PlainLock, PrivateKey, PublicKey, Signature, Uid}; +use proto::funder::messages::Currency; +use signature::signature_buff::create_response_signature_buffer; + +use crate::mutual_credit::outgoing::queue_request; + +use crate::mutual_credit::tests::utils::{ + process_operations_list, MockMutualCredit, ProcessListOutput, +}; +use crate::mutual_credit::types::{McDbClient, McOp, McRequest, McResponse, PendingTransaction}; +use crate::mutual_credit::utils::{mc_response_signature_buffer, response_op_from_mc_response}; + +async fn task_request_response() { + let currency = Currency::try_from("FST".to_owned()).unwrap(); + + let local_public_key = PublicKey::from(&[0xaa; PublicKey::len()]); + let remote_public_key = PublicKey::from(&[0xbb; PublicKey::len()]); + let balance = 0; + let in_fees = 0.into(); + let out_fees = 0.into(); + let mut mc_transaction = MockMutualCredit::new(currency.clone(), balance, in_fees, out_fees); + + // -----[RequestSendFunds]-------- + // ----------------------------- + let mut rng = DummyRandom::new(&[1u8]); + let private_key = PrivateKey::rand_gen(&mut rng); + let identity = SoftwareEd25519Identity::from_private_key(&private_key).unwrap(); + let public_key_c = identity.get_public_key(); + + let request_id = Uid::from(&[3; Uid::len()]); + let route = vec![ + PublicKey::from(&[0xaa; PublicKey::len()]), + PublicKey::from(&[0xbb; PublicKey::len()]), + public_key_c.clone(), + ]; + let invoice_hash = HashResult::from(&[0; HashResult::len()]); + let src_plain_lock = PlainLock::from(&[1; PlainLock::len()]); + + let request = McRequest { + request_id: request_id.clone(), + src_hashed_lock: src_plain_lock.hash_lock(), + route, + dest_payment: 10, + invoice_hash, + left_fees: 5, + }; + + let pending_transaction = PendingTransaction::from(request.clone()); + let local_max_debt = u128::MAX; + queue_request(&mut mc_transaction, request, ¤cy, local_max_debt) + .await + .unwrap(); + + let mc_balance = mc_transaction.get_balance().await.unwrap(); + assert_eq!(mc_balance.balance, 0); + assert_eq!(mc_balance.local_pending_debt, 10 + 5); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 0.into()); + + // -----[ResponseSendFunds]-------- + // -------------------------------- + let serial_num: u128 = 0; + + let mut response = McResponse { + request_id: request_id.clone(), + src_plain_lock: src_plain_lock.clone(), + serial_num, + signature: Signature::from(&[0; Signature::len()]), + }; + + let sign_buffer = mc_response_signature_buffer(¤cy, &response, &pending_transaction); + response.signature = identity.sign(&sign_buffer); + + let list_output = process_operations_list( + &mut mc_transaction, + vec![McOp::Response(response)], + ¤cy, + &remote_public_key, + 100, + ) + .await + .unwrap(); + assert!(matches!(list_output, ProcessListOutput::IncomingList(..))); + + // We expect that the balance has updated: + let mc_balance = mc_transaction.get_balance().await.unwrap(); + assert_eq!(mc_balance.balance, -15); + assert_eq!(mc_balance.local_pending_debt, 0); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 5.into()); +} + +#[test] +fn test_request_response() { + let test_executor = TestExecutor::new(); + let res = test_executor.run(task_request_response()); + assert!(res.is_output()); +} diff --git a/components/funder/src/mutual_credit/tests/utils.rs b/components/funder/src/mutual_credit/tests/utils.rs new file mode 100644 index 000000000..999697e7a --- /dev/null +++ b/components/funder/src/mutual_credit/tests/utils.rs @@ -0,0 +1,183 @@ +use std::collections::HashMap; + +use common::async_rpc::OpError; +use common::conn::BoxFuture; +use common::u256::U256; + +use proto::crypto::{PublicKey, Uid}; +use proto::funder::messages::{Currency, McBalance}; + +use crate::mutual_credit::incoming::{ + process_operation, IncomingMessage, ProcessOperationError, ProcessOutput, +}; +use crate::mutual_credit::types::{McDbClient, McOp, PendingTransaction}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct McPendingTransactions { + /// Pending transactions that were opened locally and not yet completed + pub local: HashMap, + /// Pending transactions that were opened remotely and not yet completed + pub remote: HashMap, +} + +impl McPendingTransactions { + fn new() -> McPendingTransactions { + McPendingTransactions { + local: HashMap::new(), + remote: HashMap::new(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MockMutualCredit { + /// Currency in use (How much is one credit worth?) + pub currency: Currency, + /// Current credit balance with respect to remote side + pub balance: McBalance, + /// Requests in progress + pub pending_transactions: McPendingTransactions, +} + +impl MockMutualCredit { + pub fn new( + // TODO: Should we move instead of take a reference here? + currency: Currency, + balance: i128, + in_fees: U256, + out_fees: U256, + ) -> MockMutualCredit { + MockMutualCredit { + currency, + balance: McBalance::new(balance, in_fees, out_fees), + pending_transactions: McPendingTransactions::new(), + } + } +} + +impl McDbClient for MockMutualCredit { + fn get_balance(&mut self) -> BoxFuture<'static, Result> { + let mc_balance = self.balance.clone(); + Box::pin(async move { Ok(mc_balance) }) + } + + fn set_balance(&mut self, balance: i128) -> BoxFuture<'static, Result<(), OpError>> { + self.balance.balance = balance; + Box::pin(async move { Ok(()) }) + } + + fn set_local_pending_debt(&mut self, debt: u128) -> BoxFuture<'static, Result<(), OpError>> { + self.balance.local_pending_debt = debt; + Box::pin(async move { Ok(()) }) + } + + fn set_remote_pending_debt(&mut self, debt: u128) -> BoxFuture<'static, Result<(), OpError>> { + self.balance.remote_pending_debt = debt; + Box::pin(async move { Ok(()) }) + } + + fn set_in_fees(&mut self, in_fees: U256) -> BoxFuture<'static, Result<(), OpError>> { + self.balance.in_fees = in_fees; + Box::pin(async move { Ok(()) }) + } + + fn set_out_fees(&mut self, out_fees: U256) -> BoxFuture<'static, Result<(), OpError>> { + self.balance.out_fees = out_fees; + Box::pin(async move { Ok(()) }) + } + + fn get_local_pending_transaction( + &mut self, + request_id: Uid, + ) -> BoxFuture<'static, Result, OpError>> { + let pending_transaction = self.pending_transactions.local.get(&request_id).cloned(); + Box::pin(async move { Ok(pending_transaction) }) + } + + fn insert_local_pending_transaction( + &mut self, + pending_transaction: PendingTransaction, + ) -> BoxFuture<'static, Result<(), OpError>> { + let _ = self + .pending_transactions + .local + .insert(pending_transaction.request_id.clone(), pending_transaction); + Box::pin(async move { Ok(()) }) + } + + fn remove_local_pending_transaction( + &mut self, + request_id: Uid, + ) -> BoxFuture<'static, Result<(), OpError>> { + let _ = self.pending_transactions.local.remove(&request_id); + Box::pin(async move { Ok(()) }) + } + + fn get_remote_pending_transaction( + &mut self, + request_id: Uid, + ) -> BoxFuture<'static, Result, OpError>> { + let pending_transaction = self.pending_transactions.remote.get(&request_id).cloned(); + Box::pin(async move { Ok(pending_transaction) }) + } + + fn insert_remote_pending_transaction( + &mut self, + pending_transaction: PendingTransaction, + ) -> BoxFuture<'static, Result<(), OpError>> { + let _ = self + .pending_transactions + .remote + .insert(pending_transaction.request_id.clone(), pending_transaction); + Box::pin(async move { Ok(()) }) + } + + fn remove_remote_pending_transaction( + &mut self, + request_id: Uid, + ) -> BoxFuture<'static, Result<(), OpError>> { + let _ = self.pending_transactions.remote.remove(&request_id); + Box::pin(async move { Ok(()) }) + } +} + +/* +#[derive(Debug)] +pub struct ProcessTransListError { + index: usize, + process_trans_error: ProcessOperationError, +} +*/ + +pub enum ProcessListOutput { + IncomingList(Vec), + Inconsistency, +} + +pub async fn process_operations_list( + mc_client: &mut impl McDbClient, + operations: Vec, + currency: &Currency, + remote_public_key: &PublicKey, + remote_max_debt: u128, +) -> Result { + let mut incoming_list = Vec::new(); + + for (index, friend_tc_op) in operations.into_iter().enumerate() { + match process_operation( + mc_client, + friend_tc_op, + currency, + remote_public_key, + remote_max_debt, + ) + .await? + { + ProcessOutput::Incoming(incoming_message) => incoming_list.push(incoming_message), + ProcessOutput::Inconsistency => { + return Ok(ProcessListOutput::Inconsistency); + } + } + } + Ok(ProcessListOutput::IncomingList(incoming_list)) +} diff --git a/components/funder/src/mutual_credit/types.rs b/components/funder/src/mutual_credit/types.rs index ae0514388..340d28277 100644 --- a/components/funder/src/mutual_credit/types.rs +++ b/components/funder/src/mutual_credit/types.rs @@ -1,221 +1,153 @@ -use std::collections::HashMap as ImHashMap; - -use common::safe_arithmetic::SafeSignedArithmetic; -use common::ser_utils::{ser_b64, ser_map_b64_any, ser_string}; - -use proto::crypto::{PublicKey, Uid}; -use proto::funder::messages::{Currency, PendingTransaction, TransactionStage}; - -/* -// TODO: Where do we need to check this value? -/// The maximum possible funder debt. -/// We don't use the full u128 because i128 can not go beyond this value. -pub const MAX_FUNDER_DEBT: u128 = (1 << 127) - 1; -*/ - -// TODO: Rename this to McIdents -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct McIdents { - /// My public key - #[serde(with = "ser_b64")] - pub local_public_key: PublicKey, - /// Friend's public key - #[serde(with = "ser_b64")] - pub remote_public_key: PublicKey, -} - -// TODO: Rename this to McBalance -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct McBalance { - /// Amount of credits this side has against the remote side. - /// The other side keeps the negation of this value. - #[serde(with = "ser_string")] - pub balance: i128, - /// Frozen credits by our side - #[serde(with = "ser_string")] - pub local_pending_debt: u128, - /// Frozen credits by the remote side - #[serde(with = "ser_string")] - pub remote_pending_debt: u128, -} - -impl McBalance { - fn new(balance: i128) -> McBalance { - McBalance { - balance, - local_pending_debt: 0, - remote_pending_debt: 0, - } - } +use common::async_rpc::AsyncOpResult; +use common::u256::U256; + +use proto::crypto::{HashResult, HashedLock, PlainLock, PublicKey, Signature, Uid}; +use proto::funder::messages::{ + CancelSendFundsOp, McBalance, RequestSendFundsOp, ResponseSendFundsOp, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PendingTransaction { + /// Id number of this request. Used to identify the whole transaction + /// over this route. + pub request_id: Uid, + /// A hash lock created by the originator of this request + pub src_hashed_lock: HashedLock, + /// Amount paid to destination + pub dest_payment: u128, + /// hash(hash(actionId) || hash(totalDestPayment) || hash(description) || hash(additional)) + /// TODO: Check if this scheme is safe? Do we need to use pbkdf instead? + pub invoice_hash: HashResult, + /// List of next nodes to transfer this request + pub route: Vec, + /// Amount of fees left to give to mediators + /// Every mediator takes the amount of fees he wants and subtracts this + /// value accordingly. + pub left_fees: u128, } -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct McPendingTransactions { - /// Pending transactions that were opened locally and not yet completed - #[serde(with = "ser_map_b64_any")] - pub local: ImHashMap, - /// Pending transactions that were opened remotely and not yet completed - #[serde(with = "ser_map_b64_any")] - pub remote: ImHashMap, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct McRequest { + /// Id number of this request. Used to identify the whole transaction + /// over this route. + pub request_id: Uid, + /// A hash lock created by the originator of this request + pub src_hashed_lock: HashedLock, + /// Amount paid to destination + pub dest_payment: u128, + /// hash(hash(actionId) || hash(totalDestPayment) || hash(description) || hash(additional)) + /// TODO: Check if this scheme is safe? Do we need to use pbkdf instead? + pub invoice_hash: HashResult, + /// List of next nodes to transfer this request + pub route: Vec, + /// Amount of fees left to give to mediators + /// Every mediator takes the amount of fees he wants and subtracts this + /// value accordingly. + pub left_fees: u128, } -impl McPendingTransactions { - fn new() -> McPendingTransactions { - McPendingTransactions { - local: ImHashMap::new(), - remote: ImHashMap::new(), - } - } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct McResponse { + /// Id number of this request. Used to identify the whole transaction + /// over this route. + pub request_id: Uid, + pub src_plain_lock: PlainLock, + /// Serial number used for this collection of invoice money. + /// This should be a u128 counter, increased by 1 for every collected + /// invoice. + pub serial_num: u128, + /// Signature{key=destinationKey}( + /// hash("FUNDS_RESPONSE") || + /// hash(request_id || src_plain_lock || dest_payment) || + /// hash(currency) || + /// serialNum || + /// invoiceHash) + /// ) + pub signature: Signature, } -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct MutualCreditState { - /// Public identities of local and remote side - pub idents: McIdents, - /// Currency in use (How much is one credit worth?) - pub currency: Currency, - /// Current credit balance with respect to remote side - pub balance: McBalance, - /// Requests in progress - pub pending_transactions: McPendingTransactions, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct McCancel { + /// Id number of this request. Used to identify the whole transaction + /// over this route. + pub request_id: Uid, } -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct MutualCredit { - state: MutualCreditState, +#[derive(Debug, Clone)] +pub enum McOp { + Request(McRequest), + Response(McResponse), + Cancel(McCancel), } -#[derive(Arbitrary, Eq, PartialEq, Debug, Clone)] -pub enum McMutation { - SetBalance(i128), - InsertLocalPendingTransaction(PendingTransaction), - RemoveLocalPendingTransaction(Uid), - SetLocalPendingTransactionStage((Uid, TransactionStage)), - InsertRemotePendingTransaction(PendingTransaction), - RemoveRemotePendingTransaction(Uid), - SetRemotePendingTransactionStage((Uid, TransactionStage)), - SetLocalPendingDebt(u128), - SetRemotePendingDebt(u128), +pub trait McDbClient { + fn get_balance(&mut self) -> AsyncOpResult; + fn set_balance(&mut self, new_balance: i128) -> AsyncOpResult<()>; + fn set_local_pending_debt(&mut self, debt: u128) -> AsyncOpResult<()>; + fn set_remote_pending_debt(&mut self, debt: u128) -> AsyncOpResult<()>; + fn set_in_fees(&mut self, in_fees: U256) -> AsyncOpResult<()>; + fn set_out_fees(&mut self, out_fees: U256) -> AsyncOpResult<()>; + fn get_local_pending_transaction( + &mut self, + request_id: Uid, + ) -> AsyncOpResult>; + fn insert_local_pending_transaction( + &mut self, + pending_transaction: PendingTransaction, + ) -> AsyncOpResult<()>; + fn remove_local_pending_transaction(&mut self, request_id: Uid) -> AsyncOpResult<()>; + fn get_remote_pending_transaction( + &mut self, + request_id: Uid, + ) -> AsyncOpResult>; + fn insert_remote_pending_transaction( + &mut self, + pending_transaction: PendingTransaction, + ) -> AsyncOpResult<()>; + fn remove_remote_pending_transaction(&mut self, request_id: Uid) -> AsyncOpResult<()>; } -impl MutualCredit { - pub fn new( - // TODO: Should we move instead of take a reference here? - local_public_key: &PublicKey, - remote_public_key: &PublicKey, - currency: &Currency, - balance: i128, - ) -> MutualCredit { - MutualCredit { - state: MutualCreditState { - idents: McIdents { - local_public_key: local_public_key.clone(), - remote_public_key: remote_public_key.clone(), - }, - currency: currency.clone(), - balance: McBalance::new(balance), - pending_transactions: McPendingTransactions::new(), - }, +impl From for PendingTransaction { + fn from(mc_request: McRequest) -> Self { + Self { + request_id: mc_request.request_id, + src_hashed_lock: mc_request.src_hashed_lock, + dest_payment: mc_request.dest_payment, + invoice_hash: mc_request.invoice_hash, + route: mc_request.route, + left_fees: mc_request.left_fees, } } +} - /// Calculate required balance for reset. - /// This would be current balance plus additional future profits. - pub fn balance_for_reset(&self) -> i128 { - self.state - .balance - .balance - .checked_add_unsigned(self.state.balance.remote_pending_debt) - .expect("Overflow when calculating balance_for_reset") - // TODO: Is this the correct formula? - // Other options: - // * balance - // * balance + remote_pending_debt - local_pending_debt - } - - pub fn state(&self) -> &MutualCreditState { - &self.state - } - - pub fn mutate(&mut self, mc_mutation: &McMutation) { - match mc_mutation { - McMutation::SetBalance(balance) => self.set_balance(*balance), - McMutation::InsertLocalPendingTransaction(pending_friend_request) => { - self.insert_local_pending_transaction(pending_friend_request) - } - McMutation::RemoveLocalPendingTransaction(request_id) => { - self.remove_local_pending_transaction(request_id) - } - McMutation::SetLocalPendingTransactionStage((request_id, stage)) => { - self.set_local_pending_transaction_stage(&request_id, stage.clone()) - } - McMutation::InsertRemotePendingTransaction(pending_friend_request) => { - self.insert_remote_pending_transaction(pending_friend_request) - } - McMutation::RemoveRemotePendingTransaction(request_id) => { - self.remove_remote_pending_transaction(request_id) - } - McMutation::SetRemotePendingTransactionStage((request_id, stage)) => { - self.set_remote_pending_transaction_stage(&request_id, stage.clone()) - } - McMutation::SetLocalPendingDebt(local_pending_debt) => { - self.set_local_pending_debt(*local_pending_debt) - } - McMutation::SetRemotePendingDebt(remote_pending_debt) => { - self.set_remote_pending_debt(*remote_pending_debt) - } +impl From for McRequest { + fn from(request_send_funds: RequestSendFundsOp) -> Self { + Self { + request_id: request_send_funds.request_id, + src_hashed_lock: request_send_funds.src_hashed_lock, + dest_payment: request_send_funds.dest_payment, + invoice_hash: request_send_funds.invoice_hash, + route: request_send_funds.route, + left_fees: request_send_funds.left_fees, } } +} - fn set_balance(&mut self, balance: i128) { - self.state.balance.balance = balance; - } - - fn insert_remote_pending_transaction(&mut self, pending_friend_request: &PendingTransaction) { - self.state.pending_transactions.remote.insert( - pending_friend_request.request_id.clone(), - pending_friend_request.clone(), - ); - } - - fn remove_remote_pending_transaction(&mut self, request_id: &Uid) { - let _ = self.state.pending_transactions.remote.remove(request_id); - } - - fn insert_local_pending_transaction(&mut self, pending_friend_request: &PendingTransaction) { - self.state.pending_transactions.local.insert( - pending_friend_request.request_id.clone(), - pending_friend_request.clone(), - ); - } - - fn remove_local_pending_transaction(&mut self, request_id: &Uid) { - let _ = self.state.pending_transactions.local.remove(request_id); - } - - fn set_remote_pending_debt(&mut self, remote_pending_debt: u128) { - self.state.balance.remote_pending_debt = remote_pending_debt; - } - - fn set_local_pending_debt(&mut self, local_pending_debt: u128) { - self.state.balance.local_pending_debt = local_pending_debt; - } - - fn set_local_pending_transaction_stage(&mut self, request_id: &Uid, stage: TransactionStage) { - self.state - .pending_transactions - .local - .get_mut(&request_id) - .unwrap() - .stage = stage; +impl From for McResponse { + fn from(response_send_funds: ResponseSendFundsOp) -> Self { + Self { + request_id: response_send_funds.request_id, + src_plain_lock: response_send_funds.src_plain_lock, + serial_num: response_send_funds.serial_num, + signature: response_send_funds.signature, + } } +} - fn set_remote_pending_transaction_stage(&mut self, request_id: &Uid, stage: TransactionStage) { - self.state - .pending_transactions - .remote - .get_mut(&request_id) - .unwrap() - .stage = stage; +impl From for McCancel { + fn from(cancel_send_funds: CancelSendFundsOp) -> Self { + Self { + request_id: cancel_send_funds.request_id, + } } } diff --git a/components/funder/src/mutual_credit/utils.rs b/components/funder/src/mutual_credit/utils.rs new file mode 100644 index 000000000..333967047 --- /dev/null +++ b/components/funder/src/mutual_credit/utils.rs @@ -0,0 +1,41 @@ +use proto::funder::messages::{Currency, ResponseSendFundsOp}; + +use signature::signature_buff::create_response_signature_buffer; + +use crate::mutual_credit::types::{McRequest, McResponse, PendingTransaction}; + +/* +pub fn pending_transaction_from_mc_request(request: McRequest) -> PendingTransaction { + PendingTransaction { + request_id: request.request_id, + src_hashed_lock: request.src_hashed_lock, + dest_payment: request.dest_payment, + invoice_hash: request.invoice_hash, + } +} +*/ + +pub fn response_op_from_mc_response(response: McResponse) -> ResponseSendFundsOp { + ResponseSendFundsOp { + request_id: response.request_id, + src_plain_lock: response.src_plain_lock, + serial_num: response.serial_num, + signature: response.signature, + } +} + +/// Calculate a response signature +pub fn mc_response_signature_buffer( + currency: &Currency, + mc_response: &McResponse, + pending_transaction: &PendingTransaction, +) -> Vec { + create_response_signature_buffer( + &pending_transaction.request_id, + currency, + pending_transaction.dest_payment, + &pending_transaction.invoice_hash, + &mc_response.src_plain_lock, + mc_response.serial_num, + ) +} diff --git a/components/funder/src/route.rs b/components/funder/src/route.rs new file mode 100644 index 000000000..a9f5ce174 --- /dev/null +++ b/components/funder/src/route.rs @@ -0,0 +1,113 @@ +use std::collections::HashSet; +use std::hash::Hash; + +use proto::consts::MAX_ROUTE_LEN; + +pub trait Route { + /// Check if the route (e.g. `FriendsRoute`) is valid. + /// A valid route must have at least 2 unique nodes, and is in one of the following forms: + /// A -- B -- C -- D -- E -- F -- A (Single cycle, first == last) + /// A -- B -- C -- D -- E -- F (A route with no repetitions) + fn is_valid(&self) -> bool; + + /// Checks if the remaining part of the route (e.g. `FriendsRoute`) is valid. + /// Compared to regular version, this one does not check for minimal unique + /// nodes amount. It returns `true` if the part is empty. + /// It does not accept routes parts with a cycle, though. + fn is_part_valid(&self) -> bool; +} + +/// Check if no element repeats twice in the slice +fn no_duplicates(array: &[T]) -> bool { + let mut seen = HashSet::new(); + for item in array { + if !seen.insert(item) { + return false; + } + } + true +} + +fn is_route_valid(route: &[T]) -> bool +where + T: Hash + Eq, +{ + if route.len() < 2 { + return false; + } + if route.len() > MAX_ROUTE_LEN { + return false; + } + + // route.len() >= 2 + let last_key = route.last().unwrap(); + if last_key == &route[0] { + // We have a first == last cycle. + if route.len() > 2 { + // We have a cycle that is long enough (no A -- A). + // We just check if it's a single cycle. + no_duplicates(&route[1..]) + } else { + // A -- A + false + } + } else { + // No first == last cycle. + // But we have to check if there is any other cycle. + no_duplicates(&route) + } +} + +fn is_route_part_valid(route: &[T]) -> bool +where + T: Hash + Eq, +{ + // Route part should not be full route. + // TODO: ensure it never is. + if route.len() >= MAX_ROUTE_LEN { + return false; + } + + no_duplicates(route) +} + +impl Route for R +where + T: Hash + Eq, + R: AsRef<[T]>, +{ + fn is_valid(&self) -> bool { + is_route_valid(self.as_ref()) + } + + fn is_part_valid(&self) -> bool { + is_route_part_valid(self.as_ref()) + } +} + +#[cfg(test)] +mod tests { + use super::Route; + + #[test] + fn test_friends_is_route_valid() { + assert_eq!([1].is_valid(), false); // too short + assert_eq!([1].is_part_valid(), true); // long enough + assert_eq!(Vec::::new().is_valid(), false); // empty route is invalid + assert_eq!(Vec::::new().is_part_valid(), true); // partial routes may be empty + + // Test cases taken from https://github.com/freedomlayer/offset/pull/215#discussion_r292327613 + assert_eq!([1, 2, 3, 4].is_valid(), true); // usual route + assert_eq!([1, 2, 3, 4, 1].is_valid(), true); // cyclic route that is at least 3 nodes long, having first item equal the last item + assert_eq!([1, 1].is_valid(), false); // cyclic route that is too short (only 2 nodes long) + assert_eq!([1, 2, 3, 2, 4].is_valid(), false); // Should have no repetitions that are not the first and last nodes. + + assert_eq!([1, 2, 3, 4].is_part_valid(), true); // usual route + assert_eq!([1, 2, 3, 4, 1].is_part_valid(), false); // should have no cycles in a partial route + assert_eq!([1, 1].is_part_valid(), false); // should have no repetitions ins a partial route + assert_eq!([1, 2, 3, 2, 4].is_part_valid(), false); // should have no repetitions in a partial route + + assert_eq!(vec![1, 2, 3, 2, 4].is_part_valid(), false); // should have no repetitions in a partial route + assert_eq!((&[1, 2, 3, 2, 4]).is_part_valid(), false); // should have no repetitions in a partial route + } +} diff --git a/components/funder/src/router/handle_config.rs b/components/funder/src/router/handle_config.rs new file mode 100644 index 000000000..f98dcd6c7 --- /dev/null +++ b/components/funder/src/router/handle_config.rs @@ -0,0 +1,387 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use database::transaction::Transaction; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResetTerms, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterInfo, + RouterOutput, RouterState, SentRelay, +}; +use crate::router::utils::index_mutation::create_index_mutation; +use crate::router::utils::move_token::is_pending_move_token; +use crate::token_channel::{ + handle_in_move_token, ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError, +}; + +pub async fn add_currency( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, +) -> Result<(), RouterError> { + let access = control.access(); + // First we make sure that the friend exists: + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + access + .router_db_client + .add_currency_config(friend_public_key.clone(), currency) + .await?; + + access.send_commands.move_token(friend_public_key); + + Ok(()) +} + +pub async fn set_remove_currency( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, +) -> Result<(), RouterError> { + // Revise implementation with respect to new design + // First we make sure that the friend exists: + if control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + control + .access() + .router_db_client + .set_currency_remove(friend_public_key.clone(), currency) + .await?; + + control.access().send_commands.move_token(friend_public_key); + Ok(()) +} + +pub async fn unset_remove_currency( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, +) -> Result<(), RouterError> { + // First we make sure that the friend exists: + if control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + control + .access() + .router_db_client + .unset_currency_remove(friend_public_key.clone(), currency) + .await?; + + control.access().send_commands.move_token(friend_public_key); + Ok(()) +} + +pub async fn set_remote_max_debt( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, + remote_max_debt: u128, +) -> Result<(), RouterError> { + let access = control.access(); + // First we make sure that the friend exists: + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + + let opt_currency_info = access + .router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await?; + if let Some(currency_info) = opt_currency_info { + // Currency exists (We don't do anything otherwise) + // Set remote max debt: + access + .router_db_client + .set_remote_max_debt(friend_public_key.clone(), currency.clone(), remote_max_debt) + .await?; + + // TODO: It is possible that the capacity was already zero, and when we changed + // remote_max_debt the capacity has somehow stayed zero. In that case we will not need to + // resend an index mutation. Currently we do send. It wastes bandwidth, but it is still + // correct. Maybe in the future we can decide more elegantly when to send an index + // mutation. + + // Create an index mutation if needed: + if currency_info.is_open { + // Currency is open: + access.output.add_index_mutation(create_index_mutation( + friend_public_key.clone(), + currency, + currency_info, + )?); + } + } + + Ok(()) +} + +pub async fn set_local_max_debt( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, + local_max_debt: u128, +) -> Result<(), RouterError> { + let access = control.access(); + // First we make sure that the friend exists: + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + + let opt_currency_info = access + .router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await?; + if let Some(currency_info) = opt_currency_info { + // Currency exists (We don't do anything otherwise) + // Set remote max debt: + access + .router_db_client + .set_local_max_debt(friend_public_key.clone(), currency.clone(), local_max_debt) + .await?; + + // TODO: It is possible that the capacity was already zero, and when we changed + // remote_max_debt the capacity has somehow stayed zero. In that case we will not need to + // resend an index mutation. Currently we do send. It wastes bandwidth, but it is still + // correct. Maybe in the future we can decide more elegantly when to send an index + // mutation. + + // Create an index mutation if needed: + if currency_info.is_open { + // Currency is open: + access.output.add_index_mutation(create_index_mutation( + friend_public_key.clone(), + currency, + currency_info, + )?); + } + } + + Ok(()) +} + +pub async fn open_currency( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + currency: Currency, +) -> Result<(), RouterError> { + let access = control.access(); + // First we make sure that the friend exists: + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + + let opt_currency_info = access + .router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await?; + + if let Some(currency_info) = opt_currency_info { + // Currency exists: + if !currency_info.is_open { + // currency is closed: + + // Open currency: + access + .router_db_client + .open_currency(friend_public_key.clone(), currency.clone()) + .await?; + + let index_mutation = create_index_mutation(friend_public_key, currency, currency_info)?; + if matches!(index_mutation, IndexMutation::UpdateFriendCurrency(..)) { + // Add index mutation: + access.output.add_index_mutation(index_mutation); + } + } + } + + Ok(()) +} + +pub async fn close_currency( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + currency: Currency, +) -> Result<(), RouterError> { + let access = control.access(); + // First we make sure that the friend exists: + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + + let opt_currency_info = access + .router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await?; + + if let Some(currency_info) = opt_currency_info { + // Currency exists: + if currency_info.is_open { + // currency is open: + + // Close currency: + access + .router_db_client + .close_currency(friend_public_key.clone(), currency.clone()) + .await?; + + // Add index mutation: + access + .output + .add_index_mutation(IndexMutation::RemoveFriendCurrency(RemoveFriendCurrency { + public_key: friend_public_key.clone(), + currency, + })); + + // Cancel all user requests pending for this currency: + while let Some(mc_request) = access + .router_db_client + .pending_user_requests_pop_front_by_currency(friend_public_key.clone()) + .await? + { + todo!(); + } + + // Cancel all requests pending for this currency: + while let Some(mc_request) = access + .router_db_client + .pending_user_requests_pop_front_by_currency(friend_public_key.clone()) + .await? + { + todo!(); + } + } + } + + Ok(()) +} + +pub async fn add_friend( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + friend_name: String, +) -> Result<(), RouterError> { + // First we make sure that the friend does not exist: + if control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_some() + { + return Ok(()); + } + + control + .access() + .router_db_client + .add_friend(friend_name, friend_public_key) + .await?; + Ok(()) +} + +pub async fn remove_friend( + control: &mut impl RouterControl, + friend_public_key: PublicKey, +) -> Result<(), RouterError> { + // TODO: A sudden change of balance is possible + // How to document this change correctly (As a friend event)? + + let tc_db_client = if let Some(tc_db_client) = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + { + tc_db_client + } else { + // No such friend exists + return Ok(()); + }; + + // TODO: Somehow report balance changes: + if tc_db_client.get_tc_status().await?.is_consistent() { + todo!(); + // tc_db_client.list_balances(&mut self) -> AsyncOpStream<(Currency, McBalance)>; + } else { + todo!(); + } + + /* + fn add_friend_event( + &mut self, + friend_public_key: PublicKey, + balances_diff: HashMap, + ) -> AsyncOpResult<()>; + */ + + control + .access() + .router_db_client + .remove_friend(friend_public_key) + .await?; + + Ok(()) +} diff --git a/components/funder/src/router/handle_friend.rs b/components/funder/src/router/handle_friend.rs new file mode 100644 index 000000000..99d286961 --- /dev/null +++ b/components/funder/src/router/handle_friend.rs @@ -0,0 +1,520 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use database::transaction::Transaction; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey, Uid}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResetTerms, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; + +use crate::mutual_credit::incoming::{ + IncomingCancelSendFundsOp, IncomingMessage, IncomingResponseSendFundsOp, +}; +use crate::mutual_credit::{McCancel, McRequest}; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterInfo, + RouterOutput, RouterState, SentRelay, +}; +use crate::router::utils::move_token::handle_in_move_token_index_mutations; +use crate::token_channel::{ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError}; + +/// Check if credit line between this node and a friend is ready +/// Works for sending requests in both directions +async fn is_credit_line_ready( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + currency: Currency, + request_id: Uid, +) -> Result { + // Friend must be online: + if !control + .access() + .ephemeral + .liveness + .is_online(&friend_public_key) + { + return Ok(false); + } + + // Currency must exist: + let currency_info = if let Some(currency_info) = control + .access() + .router_db_client + .get_currency_info(friend_public_key, currency) + .await? + { + currency_info + } else { + return Ok(false); + }; + + // Note: We bypass `is_open` check if the request is of local origin: + Ok(currency_info.is_open + || control + .access() + .router_db_client + .is_request_local_origin(request_id) + .await?) +} + +async fn incoming_message_request( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, + mut mc_request: McRequest, +) -> Result<(), RouterError> { + // Make sure that we are ready to receive this request operation + // We might not be ready in the case of currency being closed. + if !is_credit_line_ready( + control, + friend_public_key.clone(), + currency.clone(), + mc_request.request_id.clone(), + ) + .await? + { + // Return a cancel message and flush origin friend + control + .access() + .router_db_client + .pending_backwards_push_back( + friend_public_key.clone(), + BackwardsOp::Cancel( + currency.clone(), + McCancel { + request_id: mc_request.request_id.clone(), + }, + ), + ) + .await?; + + control.access().send_commands.move_token(friend_public_key); + + return Ok(()); + } + + // If directed to us, punt + if mc_request.route.is_empty() { + // Directed to us. Punt: + control + .access() + .output + .add_incoming_request(currency.clone(), mc_request.into()); + return Ok(()); + } + + // - If directed to another friend: + // - If friend exists and ready, forward to next hop + // - Otherwise, queue cancel + // Route is not empty. We need to forward the request to a friend + let next_public_key = mc_request.route.remove(0); + + let is_credit_line_ready = is_credit_line_ready( + control, + next_public_key.clone(), + currency.clone(), + mc_request.request_id.clone(), + ) + .await?; + + // Check if next public key corresponds to a friend that is ready + let should_forward = is_credit_line_ready + && !control + .access() + .router_db_client + .is_local_request_exists(mc_request.request_id.clone()) + .await?; + + // Attempt to collect fees according to rate. If credits in fees jar are insufficient, do not + // forward, and cancel the request. + todo!(); + + if should_forward { + // Queue request to friend and flush destination friend + control + .access() + .router_db_client + .pending_requests_push_back(next_public_key.clone(), currency, mc_request.into()) + .await?; + // TODO: flush_friend() is delicate. We might be able to aggregate some more pending + // requests before flushing this friend. Find out how to do this. + + control.access().send_commands.move_token(next_public_key); + } else { + // Return a cancel message and flush origin friend + control + .access() + .router_db_client + .pending_backwards_push_back( + friend_public_key.clone(), + BackwardsOp::Cancel( + currency, + McCancel { + request_id: mc_request.request_id.clone(), + }, + ), + ) + .await?; + + control.access().send_commands.move_token(friend_public_key); + } + + Ok(()) +} + +/// An incoming request was received, but due to insufficient trust we can not +/// forward this request. We return a cancel message to the sender friend +async fn incoming_message_request_cancel( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + currency: Currency, + mc_request: McRequest, +) -> Result<(), RouterError> { + if control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .ok_or(RouterError::InvalidState)? + .get_tc_status() + .await? + .is_consistent() + { + let backwards_op = BackwardsOp::Cancel( + currency, + McCancel { + request_id: mc_request.request_id, + }, + ); + + // Queue cancel to friend_public_key (Request origin) + control + .access() + .router_db_client + .pending_backwards_push_back(friend_public_key.clone(), backwards_op) + .await?; + } else { + // We have just received a cancel message from this friend. + // We expect that this friend is consistent + return Err(RouterError::InvalidState); + } + Ok(()) +} + +async fn incoming_message_response( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, + incoming_response: IncomingResponseSendFundsOp, +) -> Result<(), RouterError> { + // Gather information about request's origin: + let opt_request_origin = control + .access() + .router_db_client + .get_remote_pending_request_origin(incoming_response.incoming_response.request_id.clone()) + .await?; + let is_local_origin = control + .access() + .router_db_client + .is_request_local_origin(incoming_response.incoming_response.request_id.clone()) + .await?; + + match (is_local_origin, opt_request_origin) { + (false, None) => { + // Request was probably originated from a friend that became inconsistent. + // We have nothing to do here. + } + (true, None) => { + // Request has originated locally + + // Clear the request from local requests list + control + .access() + .router_db_client + .remove_local_request(incoming_response.incoming_response.request_id.clone()) + .await?; + + // We punt the response + control + .access() + .output + .add_incoming_response(currency.clone(), incoming_response.incoming_response); + } + (false, Some(request_origin)) => { + // Request has originated from a friend. + // We assume that friend must be consistent, otherwise it would have not been found as + // an origin. + + // Push response: + control + .access() + .router_db_client + .pending_backwards_push_back( + friend_public_key.clone(), + BackwardsOp::Response(currency, incoming_response.incoming_response), + ) + .await?; + + control.access().send_commands.move_token(friend_public_key); + } + (true, Some(request_origin)) => { + // This means the request has both originated locally, and from a friend. + // Note that this only happen during a cycle request, but a cycle always begins and + // ends at the local node. + // Therefore, this should never happen when processing a friend incoming response. + return Err(RouterError::InvalidState); + } + } + Ok(()) +} + +// TODO: This function seems too similar to `incoming_message_response` +async fn incoming_message_cancel( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + currency: Currency, + incoming_cancel: IncomingCancelSendFundsOp, +) -> Result<(), RouterError> { + // Gather information about request's origin: + let opt_request_origin = control + .access() + .router_db_client + .get_remote_pending_request_origin(incoming_cancel.incoming_cancel.request_id.clone()) + .await?; + let is_local_origin = control + .access() + .router_db_client + .is_request_local_origin(incoming_cancel.incoming_cancel.request_id.clone()) + .await?; + + match (is_local_origin, opt_request_origin) { + (false, None) => { + // Request was probably originated from a friend that became inconsistent. + // We have nothing to do here. + } + (true, None) => { + // Request has originated locally + + // Clear the request from local requests list + control + .access() + .router_db_client + .remove_local_request(incoming_cancel.incoming_cancel.request_id.clone()) + .await?; + + // We punt the cancel + control + .access() + .output + .add_incoming_cancel(currency, incoming_cancel.incoming_cancel); + } + (false, Some(request_origin)) => { + // Request has originated from a friend. + // We assume that friend must be consistent, otherwise it would have not been found as + // an origin. + + // Push cancel: + control + .access() + .router_db_client + .pending_backwards_push_back( + friend_public_key.clone(), + BackwardsOp::Cancel(currency, incoming_cancel.incoming_cancel), + ) + .await?; + + control + .access() + .send_commands + .move_token(friend_public_key.clone()); + } + (true, Some(request_origin)) => { + // This means the request has both originated locally, and from a friend. + // Note that this only happen during a cycle request, but a cycle always begins and + // ends at the local node. + // Therefore, this should never happen when processing a friend incoming cancel. + return Err(RouterError::InvalidState); + } + } + Ok(()) +} + +async fn incoming_move_token_request( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + in_move_token_request: MoveTokenRequest, +) -> Result<(), RouterError> { + let (receive_move_token_output, index_mutations) = handle_in_move_token_index_mutations( + control, + info, + in_move_token_request.move_token, + friend_public_key.clone(), + ) + .await?; + + // Add mutations: + for index_mutation in index_mutations { + control.access().output.add_index_mutation(index_mutation); + } + + match receive_move_token_output { + ReceiveMoveTokenOutput::Duplicate => { + // We should have nothing else to send at this point, otherwise we would have already + // sent it. + control + .access() + .send_commands + .move_token_allow_empty(friend_public_key.clone()); + } + ReceiveMoveTokenOutput::RetransmitOutgoing(move_token) => { + // Retransmit outgoing move token message to friend: + control + .access() + .send_commands + .move_token_allow_empty(friend_public_key.clone()); + } + ReceiveMoveTokenOutput::Received(move_token_received) => { + for (currency, incoming_message) in move_token_received.incoming_messages { + match incoming_message { + IncomingMessage::Request(mc_request) => { + incoming_message_request( + control, + info, + friend_public_key.clone(), + currency, + mc_request, + ) + .await + } + IncomingMessage::RequestCancel(mc_request) => { + incoming_message_request_cancel( + control, + friend_public_key.clone(), + currency, + mc_request, + ) + .await + } + IncomingMessage::Response(incoming_response) => { + incoming_message_response( + control, + info, + friend_public_key.clone(), + currency, + incoming_response, + ) + .await + } + IncomingMessage::Cancel(incoming_cancel) => { + incoming_message_cancel( + control, + info, + friend_public_key.clone(), + currency, + incoming_cancel, + ) + .await + } + }?; + } + + // Possibly send RequestMoveToken back to friend in one of the following cases: + if in_move_token_request.token_wanted { + // Remote side wants to have the token back, so we have to send a MoveTokenRequest + // to remote side, even if it is empty + control + .access() + .send_commands + .move_token_allow_empty(friend_public_key.clone()); + } else { + // We only send a MoveTokenRequest to remote side if we have something to send: + control + .access() + .send_commands + .move_token(friend_public_key.clone()); + }; + } + ReceiveMoveTokenOutput::ChainInconsistent(reset_terms) => { + // TODO: + // - Cancel relevant requests: + // - User pending requests + // - pending requests + // + // - Handle index mutations? + // + // - Send our reset terms + // + // - Notify to the outside? + todo!(); + } + } + + Ok(()) +} + +async fn incoming_inconsistency_error( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + reset_terms: ResetTerms, +) -> Result<(), RouterError> { + todo!(); +} + +async fn incoming_relays_update( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + relays_update: RelaysUpdate, +) -> Result<(), RouterError> { + todo!(); +} + +async fn incoming_relays_ack( + control: &mut impl RouterControl, + friend_public_key: PublicKey, + generation: u128, +) -> Result<(), RouterError> { + todo!(); +} + +pub async fn incoming_friend_message( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + friend_message: FriendMessage, +) -> Result<(), RouterError> { + match friend_message { + FriendMessage::MoveTokenRequest(move_token_request) => { + incoming_move_token_request(control, info, friend_public_key, move_token_request).await + } + FriendMessage::InconsistencyError(reset_terms) => { + incoming_inconsistency_error(control, friend_public_key, reset_terms).await + } + FriendMessage::RelaysUpdate(relays_update) => { + incoming_relays_update(control, friend_public_key, relays_update).await + } + FriendMessage::RelaysAck(generation) => { + incoming_relays_ack(control, friend_public_key, generation).await + } + } +} diff --git a/components/funder/src/router/handle_liveness.rs b/components/funder/src/router/handle_liveness.rs new file mode 100644 index 000000000..6c377fba5 --- /dev/null +++ b/components/funder/src/router/handle_liveness.rs @@ -0,0 +1,224 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use database::transaction::Transaction; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResetTerms, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterInfo, + RouterOutput, RouterState, SentRelay, +}; + +use crate::mutual_credit::McCancel; + +use crate::token_channel::{ + handle_in_move_token, ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError, +}; + +use crate::router::utils::index_mutation::create_index_mutation; +use crate::router::utils::move_token::is_pending_move_token; + +pub async fn set_friend_online( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, +) -> Result<(), RouterError> { + // First we make sure that the friend exists: + let tc_status = if let Some(tc_db_client) = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + { + tc_db_client.get_tc_status().await? + } else { + return Ok(()); + }; + + if control + .access() + .ephemeral + .liveness + .is_online(&friend_public_key) + { + // The friend is already marked as online! + return Err(RouterError::FriendAlreadyOnline); + } + control + .access() + .ephemeral + .liveness + .set_online(friend_public_key.clone()); + + // Check if we have any relays information to send to the remote side: + if let (Some(generation), relays) = control + .access() + .router_db_client + .get_last_sent_relays(friend_public_key.clone()) + .await? + { + control + .access() + .send_commands + .relays_update(friend_public_key.clone()); + } + + match tc_status { + TcStatus::ConsistentIn(_) => { + // Send an outgoing move token if we have something to send. + control + .access() + .send_commands + .move_token(friend_public_key.clone()); + } + TcStatus::ConsistentOut(move_token_out, _opt_move_token_hashed_in) => { + // Resend outgoing move token, + // possibly asking for the token if we have something to send + // Send an outgoing move token if we have something to send. + control + .access() + .send_commands + .move_token_allow_empty(friend_public_key.clone()); + } + TcStatus::Inconsistent(local_reset_terms, _opt_remote_reset_terms) => { + // Resend reset terms + control + .access() + .send_commands + .move_token_allow_empty(friend_public_key.clone()); + } + } + + // Add an index mutation for all open currencies: + let access = control.access(); + let mut open_currencies = access + .router_db_client + .list_open_currencies(friend_public_key.clone()); + while let Some(res) = open_currencies.next().await { + let (open_currency, open_currency_info) = res?; + + let index_mutation = + create_index_mutation(friend_public_key.clone(), open_currency, open_currency_info)?; + + // We only add currencies with non zero send/recv capacity + if matches!(IndexMutation::UpdateFriendCurrency, index_mutation) { + access.output.add_index_mutation(index_mutation); + } + } + + Ok(()) +} + +pub async fn set_friend_offline( + control: &mut impl RouterControl, + friend_public_key: PublicKey, +) -> Result<(), RouterError> { + let access = control.access(); + + // First we make sure that the friend exists: + let mut output = RouterOutput::new(); + if access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .is_none() + { + return Ok(()); + } + + if !access.ephemeral.liveness.is_online(&friend_public_key) { + // The friend is already marked as offline! + return Err(RouterError::FriendAlreadyOffline); + } + access.ephemeral.liveness.set_offline(&friend_public_key); + + // Cancel all pending user requests + while let Some((currency, pending_user_request)) = access + .router_db_client + .pending_user_requests_pop_front(friend_public_key.clone()) + .await? + { + // Clear the request from local requests list + access + .router_db_client + .remove_local_request(pending_user_request.request_id.clone()) + .await?; + // Send outgoing cancel to user: + access.output.add_incoming_cancel( + currency, + McCancel { + request_id: pending_user_request.request_id, + }, + ); + } + + // Cancel all pending requests + while let Some((currency, pending_request)) = access + .router_db_client + .pending_requests_pop_front(friend_public_key.clone()) + .await? + { + // Find from which friend this pending request has originated from. + // Due to inconsistencies, it is possible that this pending request has no origin (An + // orphan request) + let opt_request_origin = access + .router_db_client + .get_remote_pending_request_origin(pending_request.request_id.clone()) + .await?; + + if let Some(request_origin) = opt_request_origin { + // Currency should be the same! + if currency != request_origin.currency { + return Err(RouterError::InvalidState); + } + + // Cancel request by queue-ing a cancel into the relevant friend's queue: + access + .router_db_client + .pending_backwards_push_back( + request_origin.friend_public_key, + BackwardsOp::Cancel( + request_origin.currency, + McCancel { + request_id: pending_request.request_id, + }, + ), + ) + .await?; + } + } + + // Add index mutations + // We send index mutations to remove all currencies that are considered open + let mut open_currencies = access + .router_db_client + .list_open_currencies(friend_public_key.clone()); + while let Some(res) = open_currencies.next().await { + let (open_currency, _open_currency_info) = res?; + output.add_index_mutation(IndexMutation::RemoveFriendCurrency(RemoveFriendCurrency { + public_key: friend_public_key.clone(), + currency: open_currency, + })); + } + + Ok(()) +} diff --git a/components/funder/src/router/handle_relays.rs b/components/funder/src/router/handle_relays.rs new file mode 100644 index 000000000..ca9ce0161 --- /dev/null +++ b/components/funder/src/router/handle_relays.rs @@ -0,0 +1,170 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use database::transaction::Transaction; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResetTerms, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterState, SentRelay, +}; + +use crate::mutual_credit::incoming::IncomingMessage; +use crate::router::utils::move_token::is_pending_move_token; +use crate::token_channel::{ + handle_in_move_token, ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError, +}; + +/// Calculate max generation +/// Returns None if no generation was found: This would mean that the remote friend is fully +/// synced. +fn max_generation(sent_relays: &[SentRelay]) -> Option { + let mut opt_max_generation = None; + for sent_relay in sent_relays { + if let Some(sent_relay_generation) = &sent_relay.opt_generation { + if let Some(max_generation) = &mut opt_max_generation { + if sent_relay_generation > max_generation { + *max_generation = *sent_relay_generation; + } + } else { + opt_max_generation = Some(*sent_relay_generation); + } + } + } + opt_max_generation +} + +/// Returns whether friend's relays were updated +async fn update_friend_local_relays( + control: &mut impl RouterControl, + local_relays: HashMap, + friend_public_key: PublicKey, +) -> Result)>, RouterError> { + // First we make sure that the friend exists: + let _ = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .ok_or(RouterError::InvalidState)?; + + let sent_relays = control + .access() + .router_db_client + .get_sent_relays(friend_public_key.clone()) + .await?; + + let opt_max_generation = max_generation(&sent_relays); + let new_generation = match opt_max_generation { + Some(g) => g.checked_add(1).ok_or(RouterError::GenerationOverflow)?, + None => 0, + }; + + // Update local relays: + let mut new_sent_relays = Vec::new(); + let mut sent_relays_updated: bool = false; + let mut c_local_relays = local_relays.clone(); + for mut sent_relay in sent_relays.into_iter() { + if let Some(relay_address) = c_local_relays.remove(&sent_relay.relay_address.public_key) { + // We should update this relay: + let new_sent_relay = SentRelay { + relay_address: sent_relay.relay_address.clone(), + is_remove: false, + opt_generation: sent_relay.opt_generation, // ?? + }; + sent_relays_updated |= sent_relay != new_sent_relay; + sent_relay.is_remove = false; + } else { + // We should remove this relay from sent_relays: + if !sent_relay.is_remove { + sent_relay.is_remove = true; + sent_relay.opt_generation = Some(new_generation); + sent_relays_updated = true; + } + new_sent_relays.push(sent_relay); + } + } + + // Add remaining local relays: + for (relay_public_key, address) in c_local_relays.into_iter() { + let relay_address = RelayAddressPort { + public_key: relay_public_key, + address, + // Randomly generate a new port: + port: NodePort::rand_gen(control.access().rng), + }; + new_sent_relays.push(SentRelay { + relay_address, + is_remove: false, + opt_generation: Some(new_generation), + }); + sent_relays_updated = true; + } + + Ok(if sent_relays_updated { + // Update sent relays: + control + .access() + .router_db_client + .set_sent_relays(friend_public_key.clone(), new_sent_relays.clone()) + .await?; + Some(( + new_generation, + new_sent_relays + .into_iter() + .map(|sent_relay| sent_relay.relay_address) + .collect(), + )) + } else { + None + }) +} + +pub async fn update_local_relays( + control: &mut impl RouterControl, + router_state: &RouterState, + local_relays: HashMap, +) -> Result<(), RouterError> { + let mut opt_friend_public_key = None; + while let Some(friend_public_key) = control + .access() + .router_db_client + .get_next_friend(opt_friend_public_key) + .await? + { + opt_friend_public_key = Some(friend_public_key.clone()); + + let opt_res = + update_friend_local_relays(control, local_relays.clone(), friend_public_key.clone()) + .await?; + + // If sent_relays was updated and the friend is ready, we need to send relays to + // remote friend: + if let Some((generation, relays)) = opt_res { + control + .access() + .send_commands + .relays_update(friend_public_key.clone()); + } + } + + Ok(()) +} diff --git a/components/funder/src/router/handle_route.rs b/components/funder/src/router/handle_route.rs new file mode 100644 index 000000000..9d33e1532 --- /dev/null +++ b/components/funder/src/router/handle_route.rs @@ -0,0 +1,203 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use database::transaction::Transaction; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendTcOp, MoveToken, MoveTokenRequest, RelaysUpdate, + RequestSendFundsOp, ResetTerms, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterInfo, + RouterOutput, RouterState, SentRelay, +}; +use crate::router::utils::move_token::is_pending_move_token; + +use crate::mutual_credit::{McCancel, McRequest, McResponse}; + +use crate::token_channel::{ + handle_in_move_token, ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError, +}; + +pub async fn send_request( + control: &mut impl RouterControl, + info: &RouterInfo, + currency: Currency, + mut request: McRequest, +) -> Result<(), RouterError> { + // Make sure that the provided route is valid: + if !request.route.is_valid() { + return Err(RouterError::InvalidRoute); + } + + if control + .access() + .router_db_client + .is_local_request_exists(request.request_id.clone()) + .await? + { + // We already have a local request with the same id. Return back a cancel. + control.access().output.add_incoming_cancel( + currency, + McCancel { + request_id: request.request_id, + }, + ); + return Ok(()); + } + + // We cut the first two public keys from the route: + // Pop first route public key: + let route_local_public_key = request.route.remove(0); + if &route_local_public_key != &info.local_public_key { + return Err(RouterError::InvalidRoute); + } + // Pop second route public key: + let friend_public_key = request.route.remove(0); + + let opt_tc_db_client = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await?; + + let tc_db_client = if let Some(tc_db_client) = opt_tc_db_client { + tc_db_client + } else { + // Friend is not ready to accept a request. + // We cancel the request: + control.access().output.add_incoming_cancel( + currency, + McCancel { + request_id: request.request_id.clone(), + }, + ); + return Ok(()); + }; + + // Gather information about friend's channel and liveness: + // let tc_status = tc_db_client.get_tc_status().await?; + // let is_online = router_state.liveness.is_online(&friend_public_key); + + // Check if friend is ready: + if tc_db_client.get_tc_status().await?.is_consistent() + && control + .access() + .ephemeral + .liveness + .is_online(&friend_public_key) + { + // Keep request_id: + let request_id = request.request_id.clone(); + + // Push request: + control + .access() + .router_db_client + .pending_user_requests_push_back(friend_public_key.clone(), currency, request) + .await?; + + // Mark request as created locally, so that we remember to punt the corresponding + // response/cancel message later. + control + .access() + .router_db_client + .add_local_request(request_id) + .await?; + + control + .access() + .send_commands + .move_token(friend_public_key.clone()); + } else { + control.access().output.add_incoming_cancel( + currency, + McCancel { + request_id: request.request_id, + }, + ); + } + + Ok(()) +} + +pub async fn send_response( + control: &mut impl RouterControl, + info: &RouterInfo, + response: McResponse, +) -> Result<(), RouterError> { + let opt_request_origin = control + .access() + .router_db_client + .get_remote_pending_request_origin(response.request_id.clone()) + .await?; + + // Attempt to find which node originally sent us this request, + // so that we can forward him the response. + // If we can not find the request's origin, we discard the response. + if let Some(request_origin) = opt_request_origin { + control + .access() + .router_db_client + .pending_backwards_push_back( + request_origin.friend_public_key.clone(), + BackwardsOp::Response(request_origin.currency, response), + ) + .await?; + + control + .access() + .send_commands + .move_token(request_origin.friend_public_key); + } + + Ok(()) +} + +pub async fn send_cancel( + control: &mut impl RouterControl, + cancel: McCancel, +) -> Result<(), RouterError> { + let opt_request_origin = control + .access() + .router_db_client + .get_remote_pending_request_origin(cancel.request_id.clone()) + .await?; + + // Attempt to find which node originally sent us this request, + // so that we can forward him the response. + // If we can not find the request's origin, we discard the response. + if let Some(request_origin) = opt_request_origin { + control + .access() + .router_db_client + .pending_backwards_push_back( + request_origin.friend_public_key.clone(), + BackwardsOp::Cancel(request_origin.currency, cancel), + ) + .await?; + + control + .access() + .send_commands + .move_token(request_origin.friend_public_key); + } + + Ok(()) +} diff --git a/components/funder/src/router/handler.rs b/components/funder/src/router/handler.rs new file mode 100644 index 000000000..b5f82d393 --- /dev/null +++ b/components/funder/src/router/handler.rs @@ -0,0 +1,80 @@ +use database::transaction::Transaction; + +use crate::token_channel::TcDbClient; + +use crate::router::types::{RouterControl, RouterDbClient, RouterError, RouterInfo, RouterOp}; +use crate::router::{ + handle_config, handle_friend, handle_liveness, handle_relays, handle_route, send, +}; + +pub async fn handle_router_op( + control: &mut impl RouterControl, + info: &RouterInfo, + router_op: RouterOp, +) -> Result<(), RouterError> { + match router_op { + RouterOp::AddCurrency(friend_public_key, currency) => { + handle_config::add_currency(control, info, friend_public_key, currency).await + } + RouterOp::SetRemoveCurrency(friend_public_key, currency) => { + handle_config::set_remove_currency(control, info, friend_public_key, currency).await + } + RouterOp::UnsetRemoveCurrency(friend_public_key, currency) => { + handle_config::unset_remove_currency(control, info, friend_public_key, currency).await + } + RouterOp::SetRemoteMaxDebt(friend_public_key, currency, remote_max_debt) => { + handle_config::set_remote_max_debt( + control, + info, + friend_public_key, + currency, + remote_max_debt, + ) + .await + } + RouterOp::SetLocalMaxDebt(friend_public_key, currency, local_max_debt) => { + handle_config::set_local_max_debt( + control, + info, + friend_public_key, + currency, + local_max_debt, + ) + .await + } + RouterOp::OpenCurrency(friend_public_key, currency) => { + handle_config::open_currency(control, friend_public_key, currency).await + } + RouterOp::CloseCurrency(friend_public_key, currency) => { + handle_config::close_currency(control, friend_public_key, currency).await + } + RouterOp::AddFriend(friend_public_key, friend_name) => { + handle_config::add_friend(control, friend_public_key, friend_name).await + } + RouterOp::RemoveFriend(friend_public_key) => { + handle_config::remove_friend(control, friend_public_key).await + } + RouterOp::FriendMessage(friend_public_key, friend_message) => { + handle_friend::incoming_friend_message(control, info, friend_public_key, friend_message) + .await + } + RouterOp::SetFriendOnline(friend_public_key) => { + handle_liveness::set_friend_online(control, info, friend_public_key).await + } + RouterOp::SetFriendOffline(friend_public_key) => { + handle_liveness::set_friend_offline(control, friend_public_key).await + } + RouterOp::UpdateFriendLocalRelays(friend_public_key, friend_local_relays) => todo!(), + RouterOp::UpdateLocalRelays(local_relays) => todo!(), + RouterOp::SendRequest(currency, mc_request) => { + handle_route::send_request(control, info, currency, mc_request).await + } + RouterOp::SendResponse(mc_response) => { + handle_route::send_response(control, info, mc_response).await + } + RouterOp::SendCancel(mc_cancel) => handle_route::send_cancel(control, mc_cancel).await, + // TODO: Should also handle add/remove friend? + }?; + + send::flush_friends(control, info).await +} diff --git a/components/funder/src/router/mod.rs b/components/funder/src/router/mod.rs new file mode 100644 index 000000000..ac47a0262 --- /dev/null +++ b/components/funder/src/router/mod.rs @@ -0,0 +1,13 @@ +mod utils; + +mod handle_config; +mod handle_friend; +mod handle_liveness; +mod handle_relays; +mod handle_route; + +mod send; + +mod handler; + +mod types; diff --git a/components/funder/src/router/send.rs b/components/funder/src/router/send.rs new file mode 100644 index 000000000..df51938ff --- /dev/null +++ b/components/funder/src/router/send.rs @@ -0,0 +1,10 @@ +use crate::router::types::{RouterControl, RouterError, RouterInfo}; + +/// Send all pending MoveToken messages +pub async fn flush_friends( + control: &mut impl RouterControl, + info: &RouterInfo, +) -> Result<(), RouterError> { + // TODO: Remember to deal with index mutations here too + todo!(); +} diff --git a/components/funder/src/router/types.rs b/components/funder/src/router/types.rs new file mode 100644 index 000000000..015f11922 --- /dev/null +++ b/components/funder/src/router/types.rs @@ -0,0 +1,551 @@ +use std::collections::{HashMap, HashSet}; + +use derive_more::From; + +use common::async_rpc::{AsyncOpResult, AsyncOpStream, OpError}; +use common::u256::U256; + +use crypto::rand::CryptoRandom; +use identity::IdentityClient; + +use database::transaction::Transaction; + +use proto::app_server::messages::{NamedRelayAddress, RelayAddressPort}; +use proto::crypto::{PublicKey, Uid}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, McBalance, Rate, RequestSendFundsOp, + ResponseSendFundsOp, +}; +use proto::index_server::messages::IndexMutation; +use proto::net::messages::NetAddress; + +use crate::liveness::Liveness; +use crate::mutual_credit::{McCancel, McDbClient, McRequest, McResponse}; +use crate::token_channel::TcDbClient; +use crate::token_channel::TokenChannelError; + +#[derive(Debug, From)] +pub enum RouterError { + FriendAlreadyOnline, + FriendAlreadyOffline, + GenerationOverflow, + InvalidRoute, + InvalidState, + UnexpectedTcStatus, + TokenChannelError(TokenChannelError), + OpError(OpError), +} + +/// Router's ephemeral state (Not saved inside the database) +#[derive(Debug)] +pub struct RouterState { + pub liveness: Liveness, +} + +#[derive(Debug)] +pub enum BackwardsOp { + Response(Currency, McResponse), + Cancel(Currency, McCancel), +} + +#[derive(Debug)] +pub struct CurrencyInfo { + /// Currency rate: This is how much it costs to the remote friend to send credits through us. + pub rate: Rate, + /// Maximum amount of debt we allow to the remote side. This is the maximum rich we can get + /// from this currency relationship. + pub remote_max_debt: u128, + /// Maximum amount of debt we are willing to get into. + pub local_max_debt: u128, + /// Do we allow requests to go through this currency? + /// TODO: Find out exactly what this means. + pub is_open: bool, + /// Is locally marked for removal? + pub is_remove: bool, + /// Is mutual credit open? Show balances + pub opt_mutual_credit: Option, +} + +/* +#[derive(Debug)] +pub struct FriendInfo { + pub public_key: PublicKey, + pub name: PublicKey, + pub is_enabled: bool, +} +*/ + +#[derive(Debug)] +pub struct FriendBalance { + /// Amount of credits this side has against the remote side. + /// The other side keeps the negation of this value. + pub balance: i128, + /// Fees that were received from remote side + pub in_fees: U256, + /// Fees that were given to remote side + pub out_fees: U256, +} + +#[derive(Debug)] +pub struct FriendBalanceDiff { + /// Old balance (Before event occured) + pub old_balance: FriendBalance, + /// New balance (after event occured) + pub new_balance: FriendBalance, +} + +#[derive(Debug)] +pub struct RequestOrigin { + pub friend_public_key: PublicKey, + pub currency: Currency, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SentRelay { + pub relay_address: RelayAddressPort, + pub is_remove: bool, + pub opt_generation: Option, +} + +pub trait RouterDbClient { + type TcDbClient: TcDbClient; + fn tc_db_client( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + + /* + /// Get the current list of local relays + fn get_local_relays(&mut self) -> AsyncOpResult>; + */ + + /// Add a new friend + fn add_friend( + &mut self, + friend_name: String, + friend_public_key: PublicKey, + ) -> AsyncOpResult<()>; + + /// Remove friend + fn remove_friend(&mut self, friend_public_key: PublicKey) -> AsyncOpResult<()>; + + /// A util to iterate over friends and mutating them. + /// A None input will return the first friend. + /// A None output means that there are no more friends. + fn get_next_friend( + &mut self, + prev_friend_public_key: Option, + ) -> AsyncOpResult>; + + /// Get the last set of sent relays, together with their generation + /// Generation == None means that the sent relays were already acked + fn get_last_sent_relays( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult<(Option, Vec)>; + + /// Get detailed list of sent relays, including generation information + fn get_sent_relays(&mut self, friend_public_key: PublicKey) -> AsyncOpResult<(Vec)>; + + /// Set list of sent relays + fn set_sent_relays( + &mut self, + friend_public_key: PublicKey, + sent_relays: Vec, + ) -> AsyncOpResult<()>; + + /* + /// Update sent relays for friend `friend_public_key` with the list of current local relays. + fn update_sent_relays( + &mut self, + friend_public_key: PublicKey, + generation: u128, + sent_relays: Vec, + ) -> AsyncOpResult<()>; + */ + + /* + fn get_balance(&mut self) -> AsyncOpResult; + */ + + /* + fn peek_pending_backwards( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult; + */ + + fn add_local_request(&mut self, request_id: Uid) -> AsyncOpResult<()>; + fn remove_local_request(&mut self, request_id: Uid) -> AsyncOpResult<()>; + + fn pending_backwards_pop_front( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + + fn pending_backwards_push_back( + &mut self, + friend_public_key: PublicKey, + backwards_op: BackwardsOp, + ) -> AsyncOpResult<()>; + + fn pending_backwards_is_empty(&mut self, friend_public_key: PublicKey) -> AsyncOpResult; + + fn pending_user_requests_pop_front( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + + fn pending_user_requests_pop_front_by_currency( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + + fn pending_user_requests_push_back( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + mc_request: McRequest, + ) -> AsyncOpResult<()>; + + fn pending_user_requests_is_empty( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult; + + fn pending_requests_pop_front( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + + fn pending_requests_pop_front_by_currency( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult>; + + /// Check if a `request_id` is already in use. + /// Searches all local pending requests, and all local open transactions. + fn is_local_request_exists(&mut self, request_id: Uid) -> AsyncOpResult; + + // TODO: What happens if impossible to push, due to duplicate request id? + fn pending_requests_push_back( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + mc_request: McRequest, + ) -> AsyncOpResult<()>; + + fn pending_requests_is_empty(&mut self, friend_public_key: PublicKey) -> AsyncOpResult; + + fn list_open_currencies( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpStream<(Currency, CurrencyInfo)>; + + fn get_currency_info( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult>; + + /// Check if the origin of the request is any of the friends + /// If so, find the relevant friend + fn get_remote_pending_request_origin( + &mut self, + request_id: Uid, + ) -> AsyncOpResult>; + + /// Check if this request has originated from the local node + fn is_request_local_origin(&mut self, request_id: Uid) -> AsyncOpResult; + + fn add_currency_config( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + /// Set currency to be removed when possible + fn set_currency_remove( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + /// Unset currency removal + fn unset_currency_remove( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + fn set_remote_max_debt( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + remote_max_debt: u128, + ) -> AsyncOpResult<()>; + + fn set_local_max_debt( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + local_max_debt: u128, + ) -> AsyncOpResult<()>; + + fn open_currency( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + fn close_currency( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + fn remove_currency( + &mut self, + friend_public_key: PublicKey, + currency: Currency, + ) -> AsyncOpResult<()>; + + /* + /// Get a list of configured currencies that were not yet added as local currencies + fn currencies_to_add(&mut self, friend_public_key: PublicKey) -> AsyncOpResult>; + + /// Get a list of local currencies that can be removed at the moment. Options: + /// - Not present in remote currencies + /// - Present in remote currencies but have a zero balance + fn currencies_to_remove( + &mut self, + friend_public_key: PublicKey, + ) -> AsyncOpResult>; + */ + + /// Add currencies + /// -------------- + /// Get a list of configured currencies that were not yet added as local currencies + /// + /// Remove currencies + /// ----------------- + /// Get a list of local currencies that can be removed at the moment. Options: + /// - Not present in remote currencies + /// - Present in remote currencies but have a zero balance + fn currencies_diff(&mut self, friend_public_key: PublicKey) -> AsyncOpResult>; + + /// Add a friend event + /// Can be one of: 1. Channel reset, 2. Friend removal, 3. currency removal + fn add_friend_event( + &mut self, + friend_public_key: PublicKey, + balances_diff: HashMap, + ) -> AsyncOpResult<()>; +} + +#[derive(Debug)] +pub struct RouterOutput { + pub index_mutations: Vec, + // pub updated_remote_relays: Vec, + pub incoming_requests: Vec<(Currency, McRequest)>, + pub incoming_responses: Vec<(Currency, McResponse)>, + pub incoming_cancels: Vec<(Currency, McCancel)>, +} + +impl RouterOutput { + pub fn new() -> Self { + RouterOutput { + index_mutations: Vec::new(), + // updated_remote_relays: Vec::new(), + incoming_requests: Vec::new(), + incoming_responses: Vec::new(), + incoming_cancels: Vec::new(), + } + } + + pub fn add_index_mutation(&mut self, index_mutation: IndexMutation) { + self.index_mutations.push(index_mutation); + } + + // TODO: Add updated remote relays? + + pub fn add_incoming_request(&mut self, currency: Currency, request: McRequest) { + self.incoming_requests.push((currency, request)); + } + + pub fn add_incoming_response(&mut self, currency: Currency, response: McResponse) { + self.incoming_responses.push((currency, response)); + } + + pub fn add_incoming_cancel(&mut self, currency: Currency, cancel: McCancel) { + self.incoming_cancels.push((currency, cancel)); + } +} + +#[derive(Debug)] +pub struct RouterInfo { + pub local_public_key: PublicKey, + pub max_operations_in_batch: usize, +} + +#[derive(Debug)] +pub struct SendCommands { + /// friend_public_key -> allow_empty + move_token: HashMap, + /// Should we send relays update to remote side? + relays_update: HashSet, +} + +impl SendCommands { + pub fn new() -> Self { + Self { + move_token: HashMap::new(), + relays_update: HashSet::new(), + } + } + + /// Schedule send for a friend. + pub fn move_token(&mut self, friend_public_key: PublicKey) { + // Note: If we already set allow_empty = true, we will leave it equals to true. + let _ = self.move_token.entry(friend_public_key).or_insert(false); + } + + /// Schedule a send for a friend. Send will happen even if there is nothing to send. + pub fn move_token_allow_empty(&mut self, friend_public_key: PublicKey) { + let _ = self.move_token.insert(friend_public_key, true); + } + + /// Schedule relays update message + pub fn relays_update(&mut self, friend_public_key: PublicKey) { + self.relays_update.insert(friend_public_key); + } +} + +#[derive(Debug)] +pub struct RouterHandle<'a, R, RC> { + pub router_db_client: &'a mut RC, + pub identity_client: &'a mut IdentityClient, + pub rng: &'a mut R, + /// Friends with new pending outgoing messages + pub send_commands: SendCommands, + /// Ephemeral state: + pub ephemeral: &'a mut RouterState, + /// Router's output: + pub output: RouterOutput, +} + +#[derive(Debug)] +pub struct RouterAccess<'a, R, RC> { + pub router_db_client: &'a mut RC, + pub identity_client: &'a mut IdentityClient, + pub rng: &'a mut R, + /// Friends with new pending outgoing messages + pub send_commands: &'a mut SendCommands, + /// Ephemeral state: + pub ephemeral: &'a mut RouterState, + /// Router's output: + pub output: &'a mut RouterOutput, +} + +pub trait RouterControl { + type Rng: CryptoRandom; + type RouterDbClient: RouterDbClient; + type TcDbClient: TcDbClient + Transaction + Send; + type McDbClient: McDbClient + Send; + + fn access(&mut self) -> RouterAccess<'_, Self::Rng, Self::RouterDbClient>; +} + +impl<'a, R, RC> RouterControl for RouterHandle<'a, R, RC> +where + RC: RouterDbClient, + ::TcDbClient: Transaction + Send, + <::TcDbClient as TcDbClient>::McDbClient: Send, + R: CryptoRandom, +{ + type Rng = R; + type RouterDbClient = RC; + type TcDbClient = ::TcDbClient; + type McDbClient = <::TcDbClient as TcDbClient>::McDbClient; + + fn access(&mut self) -> RouterAccess<'_, Self::Rng, Self::RouterDbClient> { + RouterAccess { + router_db_client: self.router_db_client, + identity_client: self.identity_client, + rng: self.rng, + send_commands: &mut self.send_commands, + ephemeral: self.ephemeral, + output: &mut self.output, + } + } + + /* + fn router_db_client(&mut self) -> &mut Self::RouterDbClient { + self.router_db_client + } + fn identity_client(&mut self) -> &mut IdentityClient { + self.identity_client + } + fn rng(&mut self) -> &mut Self::Rng { + self.rng + } + fn add_pending_send(&mut self, friend_public_key: PublicKey) -> bool { + self.pending_send.insert(friend_public_key) + } + fn ephemeral(&mut self) -> &mut RouterState { + &mut self.state + } + fn output(&mut self) -> &mut RouterOutput { + &mut self.output + } + */ +} + +#[derive(Debug)] +pub enum RouterOp { + // Config + // ------ + /// (friend_public_key, currency) + AddCurrency(PublicKey, Currency), + /// (friend_public_key, currency) + SetRemoveCurrency(PublicKey, Currency), + /// (friend_public_key, currency) + UnsetRemoveCurrency(PublicKey, Currency), + /// (friend_public_key, currency, remote_max_debt) + SetRemoteMaxDebt(PublicKey, Currency, u128), + /// (friend_public_key, currency, local_max_debt) + SetLocalMaxDebt(PublicKey, Currency, u128), + /// (friend_public_key, currency) + OpenCurrency(PublicKey, Currency), + /// (friend_public_key, currency) + CloseCurrency(PublicKey, Currency), + /// (friend_public_key, friend_name) + AddFriend(PublicKey, String), + /// (friend_public_key) + RemoveFriend(PublicKey), + // Friend + // ------ + /// (friend_public_key, friend_message) + FriendMessage(PublicKey, FriendMessage), + // Livenesss + // --------- + /// (friend_public_key) + SetFriendOnline(PublicKey), + /// (friend_public_key) + SetFriendOffline(PublicKey), + // Relays + // ------ + /// (friend_public_key, friend_local_relays) + UpdateFriendLocalRelays(PublicKey, HashMap), + /// (friend_public_key, local_relays) + UpdateLocalRelays(HashMap), + // Route + // ----- + /// (currency, mc_request) + SendRequest(Currency, McRequest), + /// (mc_response) + SendResponse(McResponse), + /// (mc_cancel) + SendCancel(McCancel), +} diff --git a/components/funder/src/router/utils/flush.rs b/components/funder/src/router/utils/flush.rs new file mode 100644 index 000000000..17e32b636 --- /dev/null +++ b/components/funder/src/router/utils/flush.rs @@ -0,0 +1,91 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::token_channel::{TcDbClient, TcStatus, TokenChannelError}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterControl, RouterDbClient, RouterError, RouterInfo, + RouterOutput, RouterState, SentRelay, +}; +// use crate::router::utils::index_mutation::create_index_mutations_from_outgoing_move_token; +use crate::router::utils::move_token::{ + handle_out_move_token_index_mutations_disallow_empty, is_pending_move_token, +}; + +/// Attempt to send as much as possible through a token channel to remote side +/// Assumes that the token channel is in consistent state (Incoming / Outgoing). +pub async fn flush_friend( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, +) -> Result<(), RouterError> { + match control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .ok_or(RouterError::InvalidState)? + .get_tc_status() + .await? + { + TcStatus::ConsistentIn(_) => { + // Create an outgoing move token if we have something to send. + let opt_tuple = handle_out_move_token_index_mutations_disallow_empty( + control, + info, + friend_public_key.clone(), + ) + .await?; + + if let Some((move_token_request, index_mutations)) = opt_tuple { + // We have something to send to remote side: + + // Update index mutations: + for index_mutation in index_mutations { + control.access().output.add_index_mutation(index_mutation); + } + control.access().output.add_friend_message( + friend_public_key.clone(), + FriendMessage::MoveTokenRequest(move_token_request), + ); + } + } + TcStatus::ConsistentOut(move_token_out, _opt_move_token_hashed_in) => { + // Resend outgoing move token, + // possibly asking for the token if we have something to send + let friend_message = FriendMessage::MoveTokenRequest(MoveTokenRequest { + move_token: move_token_out, + token_wanted: is_pending_move_token(control, friend_public_key.clone()).await?, + }); + control + .access() + .output + .add_friend_message(friend_public_key.clone(), friend_message); + } + // This state is not possible, because we did manage to locate our request with this + // friend: + TcStatus::Inconsistent(..) => return Err(RouterError::UnexpectedTcStatus), + } + + Ok(()) +} diff --git a/components/funder/src/router/utils/index_mutation.rs b/components/funder/src/router/utils/index_mutation.rs new file mode 100644 index 000000000..3e75df531 --- /dev/null +++ b/components/funder/src/router/utils/index_mutation.rs @@ -0,0 +1,92 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, McBalance, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, RouterDbClient, RouterError, RouterOutput, RouterState, SentRelay, +}; +use crate::token_channel::{TcDbClient, TcStatus, TokenChannelError}; + +/// Calculate send and receive capacity for a certain currency +/// This is the number we are going to report to an index server +fn calc_capacities(currency_info: &CurrencyInfo) -> Result<(u128, u128), RouterError> { + // TODO: Should also take into account liveness? Maybe from the outside? + + if !currency_info.is_open { + return Ok((0, 0)); + } + + let mc_balance = if let Some(mc_balance) = ¤cy_info.opt_mutual_credit { + mc_balance.clone() + } else { + McBalance { + balance: 0, + local_pending_debt: 0, + remote_pending_debt: 0, + in_fees: 0.into(), + out_fees: 0.into(), + } + }; + + // local_max_debt + (balance - local_pending_debt) + let send_capacity = currency_info.local_max_debt.saturating_add_signed( + mc_balance + .balance + .checked_sub_unsigned(mc_balance.local_pending_debt) + .ok_or(RouterError::InvalidState)?, + ); + + // remote_max_debt - (balance + remote_pending_debt) + let recv_capacity = currency_info.remote_max_debt.saturating_sub_signed( + mc_balance + .balance + .checked_add_unsigned(mc_balance.remote_pending_debt) + .ok_or(RouterError::InvalidState)?, + ); + + Ok((send_capacity, recv_capacity)) +} + +/// Create one index mutation, based on a given currency info. +/// Returns IndexMutation::UpdateFriendCurrency if there is any send/recv capacity. +/// Otherwise, returns IndexMutation::RemoveFriendCurrency +pub fn create_index_mutation( + friend_public_key: PublicKey, + currency: Currency, + currency_info: CurrencyInfo, +) -> Result { + let (send_capacity, recv_capacity) = calc_capacities(¤cy_info)?; + Ok(if send_capacity == 0 && recv_capacity == 0 { + IndexMutation::RemoveFriendCurrency(RemoveFriendCurrency { + public_key: friend_public_key, + currency, + }) + } else { + IndexMutation::UpdateFriendCurrency(UpdateFriendCurrency { + public_key: friend_public_key, + currency, + send_capacity, + recv_capacity, + rate: currency_info.rate, + }) + }) +} diff --git a/components/funder/src/router/utils/mod.rs b/components/funder/src/router/utils/mod.rs new file mode 100644 index 000000000..ff8b8e871 --- /dev/null +++ b/components/funder/src/router/utils/mod.rs @@ -0,0 +1,3 @@ +// pub mod flush; +pub mod index_mutation; +pub mod move_token; diff --git a/components/funder/src/router/utils/move_token.rs b/components/funder/src/router/utils/move_token.rs new file mode 100644 index 000000000..c7b86511c --- /dev/null +++ b/components/funder/src/router/utils/move_token.rs @@ -0,0 +1,463 @@ +use std::collections::{HashMap, HashSet}; + +use futures::StreamExt; + +use derive_more::From; + +use common::async_rpc::OpError; +use common::safe_arithmetic::{SafeSignedArithmetic, SafeUnsignedArithmetic}; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddressPort; +use proto::crypto::{NodePort, PublicKey, Signature}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendMessage, FriendTcOp, MoveToken, MoveTokenRequest, + RelaysUpdate, RequestSendFundsOp, ResponseSendFundsOp, +}; +use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; +use proto::net::messages::NetAddress; + +use crypto::rand::{CryptoRandom, RandGen}; + +use database::transaction::Transaction; + +use crate::mutual_credit::{McCancel, McRequest, McResponse}; +use crate::route::Route; +use crate::router::types::{ + BackwardsOp, CurrencyInfo, FriendBalance, FriendBalanceDiff, RouterControl, RouterDbClient, + RouterError, RouterInfo, RouterOutput, RouterState, SentRelay, +}; +use crate::router::utils::index_mutation::create_index_mutation; +use crate::token_channel::{ + handle_in_move_token, OutMoveToken, ReceiveMoveTokenOutput, TcDbClient, TcOp, TcStatus, + TokenChannelError, +}; + +async fn queue_backwards_op( + control: &mut impl RouterControl, + info: &RouterInfo, + out_move_token: &mut OutMoveToken, + friend_public_key: PublicKey, + backwards_op: BackwardsOp, +) -> Result<(), RouterError> { + let tc_db_client = if let Some(tc_db_client) = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + { + tc_db_client + } else { + return Err(RouterError::InvalidState); + }; + let friend_tc_op = match backwards_op { + BackwardsOp::Response(currency, mc_response) => { + out_move_token + .queue_response(tc_db_client, currency, mc_response, &info.local_public_key) + .await?; + } + BackwardsOp::Cancel(currency, mc_cancel) => { + out_move_token + .queue_cancel(tc_db_client, currency, mc_cancel) + .await?; + } + }; + Ok(()) +} + +/// Queue a request to an `out_move_token`. +/// Handles failure by returning a cancel message to the relevant origin. +async fn queue_request( + control: &mut impl RouterControl, + info: &RouterInfo, + out_move_token: &mut OutMoveToken, + friend_public_key: PublicKey, + currency: Currency, + mc_request: McRequest, +) -> Result<(), RouterError> { + let tc_db_client = if let Some(tc_db_client) = control + .access() + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + { + tc_db_client + } else { + return Err(RouterError::InvalidState); + }; + + let res = out_move_token + .queue_request(tc_db_client, currency.clone(), mc_request.clone()) + .await?; + + match res { + Ok(mc_balance) => { + let currency_info = control + .access() + .router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await? + // If currency does not exist, we should have already cancelled this request. + .ok_or(RouterError::InvalidState)?; + + // If currency is marked for removal, and all balances are zero, remove currency: + if currency_info.is_remove + && mc_balance.local_pending_debt == 0 + && mc_balance.remote_pending_debt == 0 + && mc_balance.balance == 0 + { + // Remove currency: + control + .access() + .router_db_client + .remove_currency(friend_public_key.clone(), currency.clone()) + .await?; + + // Add event of currency removal (Friend event) + let balances_diff = { + let mut balances_diff = HashMap::new(); + let friend_balance_diff = FriendBalanceDiff { + old_balance: FriendBalance { + balance: 0, + in_fees: mc_balance.in_fees, + out_fees: mc_balance.out_fees, + }, + new_balance: FriendBalance { + balance: 0, + // TODO: Are we calculating in_fees and out_fees correctly here? + in_fees: 0.into(), + out_fees: 0.into(), + }, + }; + balances_diff.insert(currency.clone(), friend_balance_diff); + balances_diff + }; + control + .access() + .router_db_client + .add_friend_event(friend_public_key.clone(), balances_diff) + .await?; + } + } + Err(mc_cancel) => { + // We need to send a cancel message to the origin + if control + .access() + .router_db_client + .is_request_local_origin(mc_request.request_id.clone()) + .await? + { + // Request is of local origin + control.access().output.add_incoming_cancel( + currency, + McCancel { + request_id: mc_request.request_id.clone(), + }, + ); + } else { + if let Some(request_origin) = control + .access() + .router_db_client + .get_remote_pending_request_origin(mc_request.request_id.clone()) + .await? + { + // Request is of remote origin + control + .access() + .router_db_client + .pending_backwards_push_back( + request_origin.friend_public_key.clone(), + BackwardsOp::Cancel( + request_origin.currency.clone(), + McCancel { + request_id: mc_request.request_id.clone(), + }, + ), + ); + } else { + // Request is orphan, nothing to do here + } + } + } + } + Ok(()) +} + +async fn collect_currencies_operations( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, +) -> Result { + // Create a structure that aggregates operations to be sent in a single MoveToken message: + let mut out_move_token = OutMoveToken::new(); + + // Collect any pending responses and cancels: + while let Some(backwards_op) = control + .access() + .router_db_client + .pending_backwards_pop_front(friend_public_key.clone()) + .await? + { + queue_backwards_op( + control, + info, + &mut out_move_token, + friend_public_key.clone(), + backwards_op, + ) + .await?; + + // Make sure we do not exceed maximum amount of operations: + if out_move_token.len() >= info.max_operations_in_batch { + return Ok(out_move_token); + } + } + + // Collect any pending user requests: + while let Some((currency, mc_request)) = control + .access() + .router_db_client + .pending_user_requests_pop_front(friend_public_key.clone()) + .await? + { + queue_request( + control, + info, + &mut out_move_token, + friend_public_key.clone(), + currency, + mc_request, + ) + .await?; + + // Make sure we do not exceed maximum amount of operations: + if out_move_token.len() >= info.max_operations_in_batch { + return Ok(out_move_token); + } + } + + // Collect any pending requests: + while let Some((currency, mc_request)) = control + .access() + .router_db_client + .pending_requests_pop_front(friend_public_key.clone()) + .await? + { + queue_request( + control, + info, + &mut out_move_token, + friend_public_key.clone(), + currency, + mc_request, + ) + .await?; + + // Make sure we do not exceed maximum amount of operations: + if out_move_token.len() >= info.max_operations_in_batch { + return Ok(out_move_token); + } + } + + Ok(out_move_token) +} + +/// Do we have more pending currencies operations? +async fn is_pending_currencies_operations( + router_db_client: &mut impl RouterDbClient, + friend_public_key: PublicKey, +) -> Result { + Ok(!router_db_client + .pending_backwards_is_empty(friend_public_key.clone()) + .await? + || !router_db_client + .pending_user_requests_is_empty(friend_public_key.clone()) + .await? + || !router_db_client + .pending_requests_is_empty(friend_public_key.clone()) + .await?) +} + +async fn get_currencies_info( + router_db_client: &mut impl RouterDbClient, + friend_public_key: PublicKey, + currencies: &[Currency], +) -> Result, RouterError> { + let mut currencies_info = HashMap::::new(); + for currency in currencies.iter() { + let opt_currency_info = router_db_client + .get_currency_info(friend_public_key.clone(), currency.clone()) + .await?; + + if let Some(currency_info) = opt_currency_info { + currencies_info.insert(currency.clone(), currency_info); + } + } + Ok(currencies_info) +} + +fn create_index_mutations( + friend_public_key: PublicKey, + currencies_info: HashMap, +) -> Result, RouterError> { + let mut index_mutations = Vec::new(); + + // Sort currencies_info, to have deterministic results + let currencies_info_vec = { + let mut currencies_info_vec: Vec<(Currency, CurrencyInfo)> = currencies_info + .into_iter() + .map(|(currency, currency_info)| (currency, currency_info)) + .collect(); + currencies_info_vec.sort_by(|(c_a, c_a_inf), (c_b, c_b_inf)| c_a.cmp(c_b)); + currencies_info_vec + }; + + // Create an `IndexMutation` for every currency_info: + for (currency, currency_info) in currencies_info_vec { + index_mutations.push(create_index_mutation( + friend_public_key.clone(), + currency, + currency_info, + )?); + } + + Ok(index_mutations) +} + +async fn apply_out_move_token( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, + out_move_token: OutMoveToken, +) -> Result<(MoveTokenRequest, Vec), RouterError> { + let access = control.access(); + let tc_client = access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .ok_or(RouterError::InvalidState)?; + + // Send move token: + let (move_token, mentioned_currencies) = out_move_token + .finalize( + tc_client, + access.identity_client, + &info.local_public_key, + &friend_public_key, + ) + .await?; + + // Get currency info for all mentioned currencies: + let currencies_info = get_currencies_info( + control.access().router_db_client, + friend_public_key.clone(), + &mentioned_currencies, + ) + .await?; + + // For each currency, create index mutations based on currency information: + let index_mutations = create_index_mutations(friend_public_key.clone(), currencies_info)?; + + let move_token_request = MoveTokenRequest { + move_token, + token_wanted: is_pending_move_token(control, friend_public_key).await?, + }; + + Ok((move_token_request, index_mutations)) +} + +pub async fn handle_out_move_token_index_mutations_disallow_empty( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, +) -> Result)>, RouterError> { + let out_move_token = + collect_currencies_operations(control, info, friend_public_key.clone()).await?; + + // Do nothing if we have nothing to send to remote side: + if out_move_token.is_empty() { + return Ok(None); + } + + Ok(Some( + apply_out_move_token(control, info, friend_public_key, out_move_token).await?, + )) +} + +pub async fn handle_out_move_token_index_mutations_allow_empty( + control: &mut impl RouterControl, + info: &RouterInfo, + friend_public_key: PublicKey, +) -> Result<(MoveTokenRequest, Vec), RouterError> { + let out_move_token = + collect_currencies_operations(control, info, friend_public_key.clone()).await?; + + apply_out_move_token(control, info, friend_public_key, out_move_token).await +} + +/// Check if we have anything to send to a remove friend on a move token message, +/// without performing any data mutations +pub async fn is_pending_move_token( + control: &mut impl RouterControl, + friend_public_key: PublicKey, +) -> Result { + Ok(is_pending_currencies_operations( + control.access().router_db_client, + friend_public_key.clone(), + ) + .await?) +} + +pub async fn handle_in_move_token_index_mutations( + control: &mut impl RouterControl, + info: &RouterInfo, + move_token: MoveToken, + friend_public_key: PublicKey, +) -> Result<(ReceiveMoveTokenOutput, Vec), RouterError> { + // Handle incoming move token: + let access = control.access(); + let receive_move_token_output = handle_in_move_token( + access + .router_db_client + .tc_db_client(friend_public_key.clone()) + .await? + .ok_or(RouterError::InvalidState)?, + access.identity_client, + move_token, + &info.local_public_key, + &friend_public_key, + ) + .await?; + + // Get list of mentioned currencies: + let mentioned_currencies = if let ReceiveMoveTokenOutput::Received(move_token_received) = + &receive_move_token_output + { + let mentioned_currencies_set: HashSet<_> = move_token_received + .incoming_messages + .iter() + .map(|(currency, _incoming_message)| currency) + .cloned() + .collect(); + let mut mentioned_currencies_vec: Vec<_> = mentioned_currencies_set.into_iter().collect(); + // Sort currencies to make sure we get deterministic results: + mentioned_currencies_vec.sort(); + mentioned_currencies_vec + } else { + Vec::new() + }; + + // Get currency info for all mentioned currencies: + let currencies_info = get_currencies_info( + control.access().router_db_client, + friend_public_key.clone(), + &mentioned_currencies, + ) + .await?; + + // For each currency, create index mutations based on currency information: + let index_mutations = create_index_mutations(friend_public_key.clone(), currencies_info)?; + + Ok((receive_move_token_output, index_mutations)) +} diff --git a/components/funder/src/token_channel.rs b/components/funder/src/token_channel.rs deleted file mode 100644 index 13c08c3aa..000000000 --- a/components/funder/src/token_channel.rs +++ /dev/null @@ -1,1230 +0,0 @@ -use std::cmp::Ordering; -use std::convert::TryFrom; - -use im::hashset::HashSet as ImHashSet; -use std::collections::HashMap as ImHashMap; - -use common::ser_utils::ser_map_str_any; - -use signature::canonical::CanonicalSerialize; - -use crypto::hash::sha_512_256; -use crypto::identity::compare_public_key; - -use proto::crypto::{PublicKey, RandValue, Signature}; - -use proto::app_server::messages::RelayAddress; -use proto::funder::messages::{ - BalanceInfo, CountersInfo, Currency, CurrencyBalanceInfo, CurrencyOperations, McInfo, - MoveToken, TokenInfo, UnsignedMoveToken, -}; -use signature::signature_buff::hash_token_info; -use signature::verify::verify_move_token; - -use crate::mutual_credit::incoming::{ - process_operations_list, IncomingMessage, ProcessOperationOutput, ProcessTransListError, -}; -use crate::mutual_credit::outgoing::OutgoingMc; -use crate::mutual_credit::types::{McMutation, MutualCredit}; - -use crate::types::{create_hashed, create_unsigned_move_token, MoveTokenHashed}; - -#[derive(Arbitrary, Debug, Clone, Serialize, Deserialize)] -pub enum SetDirection { - Incoming(MoveTokenHashed), - Outgoing((MoveToken, TokenInfo)), -} - -#[allow(clippy::large_enum_variant)] -#[derive(Arbitrary, Debug, Clone)] -pub enum TcMutation { - McMutation((Currency, McMutation)), - SetLocalActiveCurrencies(Vec), - SetRemoteActiveCurrencies(Vec), - AddMutualCredit(Currency), - SetDirection(SetDirection), -} - -/// The currencies set to be active by two sides of the token channel. -/// Only currencies that are active on both sides (Intersection) can be used for trading. -#[derive(Arbitrary, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct ActiveCurrencies { - /// Currencies set to be active by local side - pub local: ImHashSet, - /// Currencies set to be active by remote side - pub remote: ImHashSet, -} - -impl ActiveCurrencies { - fn new() -> Self { - Self { - local: ImHashSet::new(), - remote: ImHashSet::new(), - } - } - - pub fn calc_active(&self) -> ImHashSet { - self.local.clone().intersection(self.remote.clone()) - } -} - -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct TcOutgoing { - pub move_token_out: MoveToken, - pub token_info: TokenInfo, - pub opt_prev_move_token_in: Option, -} - -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct TcIncoming { - pub move_token_in: MoveTokenHashed, -} - -#[allow(clippy::large_enum_variant)] -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub enum TcDirection { - Incoming(TcIncoming), - Outgoing(TcOutgoing), -} - -#[derive(Clone, Debug)] -pub struct TcInBorrow<'a> { - pub tc_incoming: &'a TcIncoming, - mutual_credits: &'a ImHashMap, - active_currencies: &'a ActiveCurrencies, -} - -#[derive(Clone, Debug)] -pub struct TcOutBorrow<'a, B> { - pub tc_outgoing: &'a TcOutgoing, - mutual_credits: &'a ImHashMap, - active_currencies: &'a ActiveCurrencies, -} - -#[derive(Clone, Debug)] -pub enum TcDirectionBorrow<'a, B> { - In(TcInBorrow<'a>), - Out(TcOutBorrow<'a, B>), -} - -#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] -pub struct TokenChannel { - direction: TcDirection, - #[serde(with = "ser_map_str_any")] - mutual_credits: ImHashMap, - active_currencies: ActiveCurrencies, -} - -#[derive(Debug)] -pub enum ReceiveMoveTokenError { - ChainInconsistency, - InvalidTransaction(ProcessTransListError), - InvalidSignature, - InvalidTokenInfo, - InvalidInconsistencyCounter, - MoveTokenCounterOverflow, - InvalidMoveTokenCounter, - TooManyOperations, - InvalidCurrency, - InvalidAddActiveCurrencies, - CanNotRemoveCurrencyInUse, -} - -#[derive(Debug)] -pub struct MoveTokenReceivedCurrency { - pub currency: Currency, - pub incoming_messages: Vec, -} - -#[derive(Debug)] -pub struct MoveTokenReceived { - pub mutations: Vec>, - pub currencies: Vec, - pub opt_local_relays: Option>>, -} - -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum ReceiveMoveTokenOutput { - Duplicate, - RetransmitOutgoing(MoveToken), - Received(MoveTokenReceived), - // In case of a reset, all the local pending requests will be canceled. -} - -#[derive(Debug)] -pub struct SendMoveTokenOutput { - pub unsigned_move_token: UnsignedMoveToken, - pub mutations: Vec>, - pub token_info: TokenInfo, -} - -#[derive(Debug)] -pub enum SendMoveTokenError { - LocalCurrencyAlreadyExists, - CanNotRemoveCurrencyInUse, -} - -/// Create a token from a public key -/// Currently this function puts the public key in the beginning of the signature buffer, -/// as the public key is shorter than a signature. -/// Possibly change this in the future (Maybe use a hash to spread the public key over all the -/// bytes of the signature) -/// -/// Note that the output here is not a real signature. This function is used for the first -/// deterministic initialization of a token channel. -fn token_from_public_key(public_key: &PublicKey) -> Signature { - let mut buff = [0; Signature::len()]; - buff[0..PublicKey::len()].copy_from_slice(public_key); - Signature::from(&buff) -} - -/// Generate a random nonce from public key. -/// Note that the result here is not really a random nonce. This function is used for the first -/// deterministic initialization of a token channel. -fn rand_nonce_from_public_key(public_key: &PublicKey) -> RandValue { - let public_key_hash = sha_512_256(public_key); - RandValue::try_from(&public_key_hash.as_ref()[..RandValue::len()]).unwrap() -} - -/// Create an initial move token in the relationship between two public keys. -/// To canonicalize the initial move token (Having an equal move token for both sides), we sort the -/// two public keys in some way. -fn initial_move_token( - low_public_key: &PublicKey, - high_public_key: &PublicKey, -) -> (MoveToken, TokenInfo) { - let token_info = TokenInfo { - mc: McInfo { - local_public_key: low_public_key.clone(), - remote_public_key: high_public_key.clone(), - balances: Vec::new(), - }, - counters: CountersInfo { - inconsistency_counter: 0, - move_token_counter: 0, - }, - }; - - // This is a special initialization case. - // Note that this is the only case where new_token is not a valid signature. - // We do this because we want to have synchronization between the two sides of the token - // channel, however, the remote side has no means of generating the signature (Because he - // doesn't have the private key). Therefore we use a dummy new_token instead. - let move_token = MoveToken { - old_token: token_from_public_key(&low_public_key), - currencies_operations: Vec::new(), - opt_local_relays: None, - opt_active_currencies: None, - info_hash: hash_token_info(&token_info), - rand_nonce: rand_nonce_from_public_key(&high_public_key), - new_token: token_from_public_key(&high_public_key), - }; - - (move_token, token_info) -} - -impl TokenChannel -where - B: Clone + CanonicalSerialize, -{ - pub fn new(local_public_key: &PublicKey, remote_public_key: &PublicKey) -> Self { - if compare_public_key(&local_public_key, &remote_public_key) == Ordering::Less { - // We are the first sender - let (move_token_out, token_info) = - initial_move_token(local_public_key, remote_public_key); - let tc_outgoing = TcOutgoing { - move_token_out, - token_info, - opt_prev_move_token_in: None, - }; - TokenChannel { - direction: TcDirection::Outgoing(tc_outgoing), - mutual_credits: ImHashMap::new(), - active_currencies: ActiveCurrencies::new(), - } - } else { - // We are the second sender - let (move_token_in_full, token_info) = - initial_move_token(remote_public_key, local_public_key); - let move_token_in = create_hashed::(&move_token_in_full, &token_info); - - let tc_incoming = TcIncoming { move_token_in }; - TokenChannel { - direction: TcDirection::Incoming(tc_incoming), - mutual_credits: ImHashMap::new(), - active_currencies: ActiveCurrencies::new(), - } - } - } - - pub fn get_mutual_credits(&self) -> &ImHashMap { - &self.mutual_credits - } - - pub fn get_active_currencies(&self) -> &ActiveCurrencies { - &self.active_currencies - } - - pub fn new_from_remote_reset( - reset_move_token: &MoveToken, - remote_token_info: &TokenInfo, - ) -> TokenChannel { - let mutual_credits: ImHashMap = remote_token_info - .mc - .balances - .iter() - .map(|currency_balance_info| { - ( - currency_balance_info.currency.clone(), - MutualCredit::new( - &remote_token_info.mc.remote_public_key, - &remote_token_info.mc.local_public_key, - ¤cy_balance_info.currency, - currency_balance_info - .balance_info - .balance - .checked_neg() - .unwrap(), - ), - ) - }) - .collect(); - - let active_currencies: ImHashSet<_> = mutual_credits.keys().cloned().collect(); - - let tc_incoming = TcIncoming { - move_token_in: create_hashed(&reset_move_token, remote_token_info), - }; - - TokenChannel { - direction: TcDirection::Incoming(tc_incoming), - mutual_credits, - active_currencies: ActiveCurrencies { - local: active_currencies.clone(), - remote: active_currencies, - }, - } - } - - pub fn new_from_local_reset( - reset_move_token: &MoveToken, - token_info: &TokenInfo, - opt_last_incoming_move_token: Option, - ) -> TokenChannel { - let mutual_credits: ImHashMap = token_info - .mc - .balances - .iter() - .map(|currency_balance_info| { - ( - currency_balance_info.currency.clone(), - MutualCredit::new( - &token_info.mc.local_public_key, - &token_info.mc.remote_public_key, - ¤cy_balance_info.currency, - currency_balance_info.balance_info.balance, - ), - ) - }) - .collect(); - - let active_currencies: ImHashSet<_> = mutual_credits.keys().cloned().collect(); - let tc_outgoing = TcOutgoing { - move_token_out: reset_move_token.clone(), - token_info: token_info.clone(), - opt_prev_move_token_in: opt_last_incoming_move_token, - }; - - TokenChannel { - direction: TcDirection::Outgoing(tc_outgoing), - mutual_credits, - active_currencies: ActiveCurrencies { - local: active_currencies.clone(), - remote: active_currencies, - }, - } - } - - /* - pub fn get_remote_max_debt(&self, currency: &Currency) -> u128 { - self.mutual_credits - .get(currency) - .unwrap() - .state() - .balance - .remote_max_debt - } - */ - - pub fn get_direction(&self) -> TcDirectionBorrow<'_, B> { - match &self.direction { - TcDirection::Incoming(tc_incoming) => TcDirectionBorrow::In(TcInBorrow { - tc_incoming: &tc_incoming, - mutual_credits: &self.mutual_credits, - active_currencies: &self.active_currencies, - }), - TcDirection::Outgoing(tc_outgoing) => TcDirectionBorrow::Out(TcOutBorrow { - tc_outgoing: &tc_outgoing, - mutual_credits: &self.mutual_credits, - active_currencies: &self.active_currencies, - }), - } - } - - /// Get the last incoming move token - /// If no such incoming move token exists (Maybe this is the beginning of the relationship), - /// returns None. - pub fn get_last_incoming_move_token_hashed(&self) -> Option<&MoveTokenHashed> { - match &self.direction { - TcDirection::Incoming(tc_incoming) => Some(&tc_incoming.move_token_in), - TcDirection::Outgoing(tc_outgoing) => match &tc_outgoing.opt_prev_move_token_in { - None => None, - Some(prev_move_token_in) => Some(prev_move_token_in), - }, - } - } - - pub fn mutate(&mut self, d_mutation: &TcMutation) { - match d_mutation { - TcMutation::McMutation((currency, mc_mutation)) => { - let mutual_credit = self.mutual_credits.get_mut(currency).unwrap(); - mutual_credit.mutate(mc_mutation); - } - TcMutation::SetDirection(ref set_direction) => { - self.direction = match set_direction { - SetDirection::Incoming(friend_move_token_hashed) => { - let tc_incoming = TcIncoming { - move_token_in: friend_move_token_hashed.clone(), - }; - TcDirection::Incoming(tc_incoming) - } - SetDirection::Outgoing((friend_move_token, token_info)) => { - let tc_outgoing = TcOutgoing { - move_token_out: friend_move_token.clone(), - token_info: token_info.clone(), - opt_prev_move_token_in: self - .get_last_incoming_move_token_hashed() - .cloned(), - }; - TcDirection::Outgoing(tc_outgoing) - } - }; - } - TcMutation::SetLocalActiveCurrencies(ref currencies) => { - self.active_currencies.local = currencies.iter().cloned().collect(); - } - TcMutation::SetRemoteActiveCurrencies(ref currencies) => { - self.active_currencies.remote = currencies.iter().cloned().collect(); - } - TcMutation::AddMutualCredit(ref currency) => { - assert!(self.active_currencies.local.contains(currency)); - assert!(self.active_currencies.remote.contains(currency)); - - let token_info = match &self.direction { - TcDirection::Incoming(tc_incoming) => { - tc_incoming.move_token_in.token_info.clone().flip() - } - TcDirection::Outgoing(tc_outgoing) => tc_outgoing.token_info.clone(), - }; - - let balance = 0; - let new_mutual_credit = MutualCredit::new( - &token_info.mc.local_public_key, - &token_info.mc.remote_public_key, - currency, - balance, - ); - let res = self - .mutual_credits - .insert(currency.clone(), new_mutual_credit); - // Make sure that this currency was not already present: - assert!(res.is_none()); - } - } - } - - pub fn get_inconsistency_counter(&self) -> u64 { - match &self.direction { - TcDirection::Incoming(tc_incoming) => { - tc_incoming - .move_token_in - .token_info - .counters - .inconsistency_counter - } - TcDirection::Outgoing(tc_outgoing) => { - tc_outgoing.token_info.counters.inconsistency_counter - } - } - } - - pub fn get_outgoing(&self) -> Option> { - match self.get_direction() { - TcDirectionBorrow::In(_) => None, - TcDirectionBorrow::Out(tc_out_borrow) => Some(tc_out_borrow), - } - } - - pub fn get_incoming(&self) -> Option> { - match self.get_direction() { - TcDirectionBorrow::In(tc_in_borrow) => Some(tc_in_borrow), - TcDirectionBorrow::Out(_) => None, - } - } - - pub fn simulate_receive_move_token( - &self, - new_move_token: MoveToken, - remote_max_debts: &ImHashMap, - ) -> Result, ReceiveMoveTokenError> { - match &self.get_direction() { - TcDirectionBorrow::In(tc_in_borrow) => tc_in_borrow.handle_incoming(new_move_token), - TcDirectionBorrow::Out(tc_out_borrow) => { - tc_out_borrow.handle_incoming(new_move_token, remote_max_debts) - } - } - } -} - -impl<'a> TcInBorrow<'a> { - /// Create a full TokenChannel (Incoming direction) - fn create_token_channel(&self) -> TokenChannel { - TokenChannel { - direction: TcDirection::Incoming(self.tc_incoming.clone()), - mutual_credits: self.mutual_credits.clone(), - active_currencies: self.active_currencies.clone(), - } - } - - /// Handle an incoming move token during Incoming direction: - fn handle_incoming( - &self, - new_move_token: MoveToken, - ) -> Result, ReceiveMoveTokenError> - where - B: CanonicalSerialize + Clone, - { - // We compare the whole move token message and not just the signature (new_token) - // because we don't check the signature in this flow. - if self.tc_incoming.move_token_in - == create_hashed(&new_move_token, &self.tc_incoming.move_token_in.token_info) - { - // Duplicate - Ok(ReceiveMoveTokenOutput::Duplicate) - } else { - // Inconsistency - Err(ReceiveMoveTokenError::ChainInconsistency) - } - } - - pub fn simulate_send_move_token( - &self, - currencies_operations: Vec, - opt_local_relays: Option>>, - opt_active_currencies: Option>, - rand_nonce: RandValue, - ) -> Result, SendMoveTokenError> - where - B: CanonicalSerialize + Clone, - { - // We create a clone `token_channel` on which we are going to apply all the mutations. - // Eventually this cloned TokenChannel is discarded, and we only output the applied mutations. - let mut token_channel = self.create_token_channel(); - let tc_in_borrow = token_channel.get_incoming().unwrap(); - - let mut tc_mutations = Vec::new(); - - if let Some(active_currencies) = opt_active_currencies.as_ref() { - for mutual_credit_currency in tc_in_borrow.mutual_credits.keys() { - if !active_currencies.contains(&mutual_credit_currency) { - return Err(SendMoveTokenError::CanNotRemoveCurrencyInUse); - } - } - - let mutation = TcMutation::SetLocalActiveCurrencies(active_currencies.clone()); - token_channel.mutate(&mutation); - tc_mutations.push(mutation); - - let tc_in_borrow = token_channel.get_incoming().unwrap(); - let active_currencies = &tc_in_borrow.active_currencies; - - // Find the new currencies we need to initialize. - // Calculate: - // (local ^ remote) \ mutual_credit_currencies: - let intersection = active_currencies - .remote - .clone() - .intersection(active_currencies.local.clone()); - - let mutual_credit_currencies: ImHashSet<_> = - tc_in_borrow.mutual_credits.keys().cloned().collect(); - - let new_currencies = intersection.relative_complement(mutual_credit_currencies); - - for new_currency in new_currencies { - let mutation = TcMutation::AddMutualCredit(new_currency.clone()); - token_channel.mutate(&mutation); - tc_mutations.push(mutation); - } - } - - // Update mutual credits: - for currency_operations in ¤cies_operations { - let tc_in_borrow = token_channel.get_incoming().unwrap(); - let mutual_credit = tc_in_borrow - .mutual_credits - .get(¤cy_operations.currency) - .unwrap() - .clone(); - - let mut outgoing_mc = OutgoingMc::new(&mutual_credit); - for op in ¤cy_operations.operations { - let mc_mutations = outgoing_mc.queue_operation(op).unwrap(); - for mc_mutation in mc_mutations { - let mutation = TcMutation::McMutation(( - currency_operations.currency.clone(), - mc_mutation.clone(), - )); - token_channel.mutate(&mutation); - tc_mutations.push(mutation); - } - } - } - - let tc_in_borrow = token_channel.get_incoming().unwrap(); - - let mut balances: Vec<_> = tc_in_borrow - .mutual_credits - .iter() - .map(|(currency, mutual_credit)| CurrencyBalanceInfo { - currency: currency.clone(), - balance_info: BalanceInfo { - balance: mutual_credit.state().balance.balance, - local_pending_debt: mutual_credit.state().balance.local_pending_debt, - remote_pending_debt: mutual_credit.state().balance.remote_pending_debt, - }, - }) - .collect(); - - // Canonicalize balances: - balances.sort_by(|cbi1, cbi2| cbi1.currency.cmp(&cbi2.currency)); - - let tc_in_borrow = token_channel.get_incoming().unwrap(); - let cur_token_info = &tc_in_borrow.tc_incoming.move_token_in.token_info; - - let token_info = TokenInfo { - mc: McInfo { - local_public_key: cur_token_info.mc.remote_public_key.clone(), - remote_public_key: cur_token_info.mc.local_public_key.clone(), - balances, - }, - counters: CountersInfo { - move_token_counter: cur_token_info.counters.move_token_counter.wrapping_add(1), - inconsistency_counter: cur_token_info.counters.inconsistency_counter, - }, - }; - - let unsigned_move_token = create_unsigned_move_token( - currencies_operations, - opt_local_relays, - opt_active_currencies, - &token_info, - tc_in_borrow.tc_incoming.move_token_in.new_token.clone(), - rand_nonce, - ); - - Ok(SendMoveTokenOutput { - unsigned_move_token, - mutations: tc_mutations, - token_info, - }) - } - - pub fn create_outgoing_mc(&self, currency: &Currency) -> Option { - Some(OutgoingMc::new(self.mutual_credits.get(currency)?)) - } -} - -impl<'a, B> TcOutBorrow<'a, B> -where - B: Clone + CanonicalSerialize, -{ - /// Handle an incoming move token during Outgoing direction: - fn handle_incoming( - &self, - new_move_token: MoveToken, - remote_max_debts: &ImHashMap, - ) -> Result, ReceiveMoveTokenError> { - if new_move_token.old_token == self.tc_outgoing.move_token_out.new_token { - Ok(ReceiveMoveTokenOutput::Received( - self.handle_incoming_token_match(new_move_token, remote_max_debts)?, - )) - // self.outgoing_to_incoming(friend_move_token, new_move_token) - } else if self.tc_outgoing.move_token_out.old_token == new_move_token.new_token { - // We should retransmit our move token message to the remote side. - Ok(ReceiveMoveTokenOutput::RetransmitOutgoing( - self.tc_outgoing.move_token_out.clone(), - )) - } else { - Err(ReceiveMoveTokenError::ChainInconsistency) - } - } - - /// Create a full TokenChannel (Outgoing direction) - fn create_token_channel(&self) -> TokenChannel { - TokenChannel { - direction: TcDirection::Outgoing(self.tc_outgoing.clone()), - mutual_credits: self.mutual_credits.clone(), - active_currencies: self.active_currencies.clone(), - } - } - - fn handle_incoming_token_match( - &self, - new_move_token: MoveToken, - remote_max_debts: &ImHashMap, - ) -> Result, ReceiveMoveTokenError> { - // We create a clone `token_channel` on which we are going to apply all the mutations. - // Eventually this cloned TokenChannel is discarded, and we only output the applied mutations. - let mut token_channel = self.create_token_channel(); - let tc_out_borrow = token_channel.get_outgoing().unwrap(); - - // Verify signature: - // Note that we only verify the signature here, and not at the Incoming part. - // This allows the genesis move token to occur smoothly, even though its signature - // is not correct. - let remote_public_key = &tc_out_borrow.tc_outgoing.token_info.mc.remote_public_key; - if !verify_move_token(new_move_token.clone(), remote_public_key) { - return Err(ReceiveMoveTokenError::InvalidSignature); - } - - // Aggregate results for every currency: - let mut move_token_received = MoveTokenReceived { - mutations: Vec::new(), - currencies: Vec::new(), - opt_local_relays: new_move_token.opt_local_relays.clone(), - }; - - // Handle active_currencies: - if let Some(active_currencies) = new_move_token.opt_active_currencies.as_ref() { - for mutual_credit_currency in tc_out_borrow.mutual_credits.keys() { - if !active_currencies.contains(&mutual_credit_currency) { - return Err(ReceiveMoveTokenError::CanNotRemoveCurrencyInUse); - } - } - - let mutation = TcMutation::SetRemoteActiveCurrencies(active_currencies.clone()); - token_channel.mutate(&mutation); - move_token_received.mutations.push(mutation); - - let tc_out_borrow = token_channel.get_outgoing().unwrap(); - let active_currencies = &tc_out_borrow.active_currencies; - - // Find the new currencies we need to initialize. - // Calculate: - // (local ^ remote) \ mutual_credit_currencies: - let intersection = active_currencies - .remote - .clone() - .intersection(active_currencies.local.clone()); - - let mutual_credit_currencies: ImHashSet<_> = - tc_out_borrow.mutual_credits.keys().cloned().collect(); - - let new_currencies = intersection.relative_complement(mutual_credit_currencies); - - for new_currency in new_currencies { - let mutation = TcMutation::AddMutualCredit(new_currency.clone()); - token_channel.mutate(&mutation); - move_token_received.mutations.push(mutation); - } - } - - // Attempt to apply operations for every currency: - for currency_operations in &new_move_token.currencies_operations { - let tc_out_borrow = token_channel.get_outgoing().unwrap(); - let mut mutual_credit = tc_out_borrow - .mutual_credits - .get(¤cy_operations.currency) - .ok_or(ReceiveMoveTokenError::InvalidCurrency)? - .clone(); - - let remote_max_debt = remote_max_debts - .get(¤cy_operations.currency) - .cloned() - .unwrap_or(0); - - let outputs = process_operations_list( - &mut mutual_credit, - currency_operations.operations.clone(), - remote_max_debt, - ) - .map_err(ReceiveMoveTokenError::InvalidTransaction)?; - - let mut incoming_messages = Vec::new(); - - // We apply mutations on this token channel, to verify stated balance values - // let mut check_mutual_credit = mutual_credit.clone(); - - for output in outputs { - let ProcessOperationOutput { - incoming_message, - mc_mutations, - } = output; - - if let Some(funds) = incoming_message { - incoming_messages.push(funds); - } - for mc_mutation in mc_mutations { - let mutation = TcMutation::McMutation(( - currency_operations.currency.clone(), - mc_mutation.clone(), - )); - token_channel.mutate(&mutation); - move_token_received.mutations.push(mutation); - } - } - - let move_token_received_currency = MoveTokenReceivedCurrency { - currency: currency_operations.currency.clone(), - incoming_messages, - }; - - move_token_received - .currencies - .push(move_token_received_currency); - } - - // Create what we expect to be TokenInfo (From the point of view of remote side): - let tc_out_borrow = token_channel.get_outgoing().unwrap(); - let mut expected_balances: Vec<_> = tc_out_borrow - .mutual_credits - .iter() - .map(|(currency, mc)| CurrencyBalanceInfo { - currency: currency.clone(), - balance_info: BalanceInfo { - balance: mc.state().balance.balance, - local_pending_debt: mc.state().balance.local_pending_debt, - remote_pending_debt: mc.state().balance.remote_pending_debt, - }, - }) - .collect(); - - // Canonicalize: - expected_balances.sort_by(|cbi1, cbi2| cbi1.currency.cmp(&cbi2.currency)); - - let expected_token_info = TokenInfo { - mc: McInfo { - local_public_key: tc_out_borrow - .tc_outgoing - .token_info - .mc - .local_public_key - .clone(), - remote_public_key: tc_out_borrow - .tc_outgoing - .token_info - .mc - .remote_public_key - .clone(), - balances: expected_balances, - }, - counters: CountersInfo { - inconsistency_counter: tc_out_borrow - .tc_outgoing - .token_info - .counters - .inconsistency_counter, - move_token_counter: tc_out_borrow - .tc_outgoing - .token_info - .counters - .move_token_counter - .checked_add(1) - .ok_or(ReceiveMoveTokenError::MoveTokenCounterOverflow)?, - }, - } - .flip(); - - // Verify stated balances: - let info_hash = hash_token_info(&expected_token_info); - if new_move_token.info_hash != info_hash { - return Err(ReceiveMoveTokenError::InvalidTokenInfo); - } - - move_token_received - .mutations - .push(TcMutation::SetDirection(SetDirection::Incoming( - create_hashed(&new_move_token, &expected_token_info), - ))); - - Ok(move_token_received) - } - - /// Get the current outgoing move token - pub fn create_outgoing_move_token(&self) -> MoveToken { - self.tc_outgoing.move_token_out.clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use proto::crypto::PrivateKey; - // use proto::funder::messages::FriendTcOp; - - use crypto::identity::Identity; - use crypto::identity::SoftwareEd25519Identity; - use crypto::rand::RandGen; - use crypto::test_utils::DummyRandom; - - use signature::signature_buff::move_token_signature_buff; - - /// A helper function to sign an UnsignedMoveToken using an identity: - fn dummy_sign_move_token( - unsigned_move_token: UnsignedMoveToken, - identity: &I, - ) -> MoveToken - where - B: CanonicalSerialize + Clone, - I: Identity, - { - let signature_buff = move_token_signature_buff(unsigned_move_token.clone()); - - MoveToken { - old_token: unsigned_move_token.old_token, - currencies_operations: unsigned_move_token.currencies_operations, - opt_local_relays: unsigned_move_token.opt_local_relays, - opt_active_currencies: unsigned_move_token.opt_active_currencies, - info_hash: unsigned_move_token.info_hash, - rand_nonce: unsigned_move_token.rand_nonce, - new_token: identity.sign(&signature_buff), - } - } - - #[test] - fn test_initial_direction() { - let pk_a = PublicKey::from(&[0xaa; PublicKey::len()]); - let pk_b = PublicKey::from(&[0xbb; PublicKey::len()]); - let token_channel_a_b = TokenChannel::::new(&pk_a, &pk_b); - let token_channel_b_a = TokenChannel::::new(&pk_b, &pk_a); - - // Only one of those token channels is outgoing: - let is_a_b_outgoing = token_channel_a_b.get_outgoing().is_some(); - let is_b_a_outgoing = token_channel_b_a.get_outgoing().is_some(); - assert!(is_a_b_outgoing ^ is_b_a_outgoing); - - let (out_tc, in_tc) = if is_a_b_outgoing { - (token_channel_a_b, token_channel_b_a) - } else { - (token_channel_b_a, token_channel_a_b) - }; - - let out_hashed = match &out_tc.direction { - TcDirection::Incoming(_) => unreachable!(), - TcDirection::Outgoing(outgoing) => { - create_hashed(&outgoing.move_token_out, &outgoing.token_info) - } - }; - - let in_hashed = match &in_tc.direction { - TcDirection::Outgoing(_) => unreachable!(), - TcDirection::Incoming(incoming) => &incoming.move_token_in, - }; - - assert_eq!(&out_hashed, in_hashed); - - // assert_eq!(create_hashed(&out_tc.move_token_out), in_tc.move_token_in); - // assert_eq!(out_tc.get_cur_move_token_hashed(), in_tc.get_cur_move_token_hashed()); - let tc_outgoing = match out_tc.get_direction() { - TcDirectionBorrow::Out(tc_out_borrow) => tc_out_borrow.tc_outgoing, - TcDirectionBorrow::In(_) => unreachable!(), - }; - assert!(tc_outgoing.opt_prev_move_token_in.is_none()); - } - - /// Sort the two identity client. - /// The result will be a pair where the first is initially configured to have outgoing message, - /// and the second is initially configured to have incoming message. - fn sort_sides(identity1: I, identity2: I) -> (I, I) - where - I: Identity, - { - let pk1 = identity1.get_public_key(); - let pk2 = identity2.get_public_key(); - let token_channel12 = TokenChannel::::new(&pk1, &pk2); // (local, remote) - if token_channel12.get_outgoing().is_some() { - (identity1, identity2) - } else { - (identity2, identity1) - } - } - /// Before: tc1: outgoing, tc2: incoming - /// Send AddCurrency: tc2 -> tc1 - /// After: tc1: incoming, tc2: outgoing - fn add_currencies( - _identity1: &I, - identity2: &I, - tc1: &mut TokenChannel, - tc2: &mut TokenChannel, - currencies: &[Currency], - ) where - I: Identity, - { - assert!(tc1.get_outgoing().is_some()); - assert!(!tc2.get_outgoing().is_some()); - - let tc2_in_borrow = tc2.get_incoming().unwrap(); - let currencies_operations = vec![]; - - let rand_nonce = RandValue::from(&[7; RandValue::len()]); - let opt_local_relays = None; - let opt_active_currencies = Some(currencies.to_vec()); - - let SendMoveTokenOutput { - unsigned_move_token, - mutations, - token_info, - } = tc2_in_borrow - .simulate_send_move_token( - currencies_operations, - opt_local_relays, - opt_active_currencies, - rand_nonce, - ) - .unwrap(); - - for tc_mutation in mutations { - tc2.mutate(&tc_mutation); - } - - // This is the only mutation we can not produce from inside TokenChannel, because it - // requires a signature: - let friend_move_token = dummy_sign_move_token(unsigned_move_token, identity2); - let tc_mutation = TcMutation::SetDirection(SetDirection::Outgoing(( - friend_move_token.clone(), - token_info.clone(), - ))); - tc2.mutate(&tc_mutation); - - assert!(tc2.get_outgoing().is_some()); - - let receive_move_token_output = tc1 - .simulate_receive_move_token(friend_move_token.clone(), &ImHashMap::new()) - .unwrap(); - - let move_token_received = match receive_move_token_output { - ReceiveMoveTokenOutput::Received(move_token_received) => move_token_received, - _ => unreachable!(), - }; - - assert!(move_token_received.currencies.is_empty()); - - // let mut seen_mc_mutation = false; - // let mut seen_set_direction = false; - - for tc_mutation in &move_token_received.mutations { - tc1.mutate(tc_mutation); - } - - assert!(!tc1.get_outgoing().is_some()); - match &tc1.direction { - TcDirection::Outgoing(_) => unreachable!(), - TcDirection::Incoming(tc_incoming) => { - assert_eq!( - tc_incoming.move_token_in, - create_hashed(&friend_move_token, &token_info) - ); - } - }; - - for currency in currencies { - assert!(tc2.active_currencies.local.contains(currency)); - assert!(tc1.active_currencies.remote.contains(currency)); - } - } - - /* - /// Before: tc1: outgoing, tc2: incoming - /// Send SetRemoteMaxDebt: tc2 -> tc1 - /// After: tc1: incoming, tc2: outgoing - fn set_remote_max_debt21( - _identity1: &I, - identity2: &I, - tc1: &mut TokenChannel, - tc2: &mut TokenChannel, - currency: &Currency, - ) where - I: Identity, - { - assert!(tc1.get_outgoing().is_some()); - assert!(tc2.get_incoming().is_some()); - - let tc2_in_borrow = tc2.get_incoming().unwrap(); - // let mut outgoing_mc = tc2_in_borrow.create_outgoing_mc(¤cy).unwrap(); - // - // let friend_tc_op = FriendTcOp::SetRemoteMaxDebt(100); - // - // let mc_mutations = outgoing_mc.queue_operation(&friend_tc_op).unwrap(); - let currency_operations = CurrencyOperations { - currency: currency.clone(), - operations: vec![friend_tc_op], - }; - let currencies_operations = vec![currency_operations]; - - let rand_nonce = RandValue::from(&[5; RandValue::len()]); - let opt_local_relays = None; - let opt_active_currencies = None; - - let SendMoveTokenOutput { - unsigned_move_token, - mutations, - token_info, - } = tc2_in_borrow - .simulate_send_move_token( - currencies_operations, - opt_local_relays, - opt_active_currencies, - rand_nonce, - ) - .unwrap(); - - for tc_mutation in mutations { - tc2.mutate(&tc_mutation); - } - - let friend_move_token = dummy_sign_move_token(unsigned_move_token, identity2); - let tc_mutation = TcMutation::SetDirection(SetDirection::Outgoing(( - friend_move_token.clone(), - token_info.clone(), - ))); - tc2.mutate(&tc_mutation); - - assert!(tc2.get_outgoing().is_some()); - - let receive_move_token_output = tc1 - .simulate_receive_move_token(friend_move_token.clone()) - .unwrap(); - - let move_token_received = match receive_move_token_output { - ReceiveMoveTokenOutput::Received(move_token_received) => move_token_received, - _ => unreachable!(), - }; - - assert!(move_token_received.currencies[0] - .incoming_messages - .is_empty()); - assert_eq!(move_token_received.mutations.len(), 2); - - let mut seen_mc_mutation = false; - let mut seen_set_direction = false; - - for i in 0..2 { - match &move_token_received.mutations[i] { - TcMutation::McMutation(mc_mutation) => { - seen_mc_mutation = true; - assert_eq!( - mc_mutation, - &(currency.clone(), McMutation::SetLocalMaxDebt(100)) - ); - } - TcMutation::SetDirection(set_direction) => { - seen_set_direction = true; - match set_direction { - SetDirection::Incoming(incoming_friend_move_token) => assert_eq!( - &create_hashed(&friend_move_token, &token_info), - incoming_friend_move_token - ), - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - assert!(seen_mc_mutation && seen_set_direction); - - for tc_mutation in &move_token_received.mutations { - tc1.mutate(tc_mutation); - } - - assert!(!tc1.get_outgoing().is_some()); - match &tc1.direction { - TcDirection::Outgoing(_) => unreachable!(), - TcDirection::Incoming(tc_incoming) => { - assert_eq!( - tc_incoming.move_token_in, - create_hashed(&friend_move_token, &token_info) - ); - } - }; - - assert_eq!( - tc1.mutual_credits - .get(¤cy) - .unwrap() - .state() - .balance - .local_max_debt, - 100 - ); - } - */ - - /// This tests sends a SetRemoteMaxDebt(100) in both ways. - #[test] - fn test_simulate_receive_move_token_basic() { - let currency = Currency::try_from("FST".to_owned()).unwrap(); - - let mut rng1 = DummyRandom::new(&[1u8]); - let pkcs8 = PrivateKey::rand_gen(&mut rng1); - let identity1 = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); - - let mut rng2 = DummyRandom::new(&[2u8]); - let pkcs8 = PrivateKey::rand_gen(&mut rng2); - let identity2 = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); - - let (identity1, identity2) = sort_sides(identity1, identity2); - - let pk1 = identity1.get_public_key(); - let pk2 = identity2.get_public_key(); - let mut tc1 = TokenChannel::::new(&pk1, &pk2); // (local, remote) - let mut tc2 = TokenChannel::::new(&pk2, &pk1); // (local, remote) - - // Current state: tc1 --> tc2 - // tc1: outgoing - // tc2: incoming - add_currencies( - &identity1, - &identity2, - &mut tc1, - &mut tc2, - &[currency.clone()], - ); - - // Current state: tc2 --> tc1 - // tc1: incoming - // tc2: outgoing - add_currencies( - &identity2, - &identity1, - &mut tc2, - &mut tc1, - &[currency.clone()], - ); - - // Current state: tc1 --> tc2 - // tc1: outgoing - // tc2: incoming - // set_remote_max_debt21(&identity1, &identity2, &mut tc1, &mut tc2, ¤cy); - - // Current state: tc2 --> tc1 - // tc1: incoming - // tc2: outgoing - // set_remote_max_debt21(&identity2, &identity1, &mut tc2, &mut tc1, ¤cy); - } - - // TODO: Add more tests. - // - Test behaviour of Duplicate, ChainInconsistency -} diff --git a/components/funder/src/token_channel/mod.rs b/components/funder/src/token_channel/mod.rs new file mode 100644 index 000000000..c9c897f8e --- /dev/null +++ b/components/funder/src/token_channel/mod.rs @@ -0,0 +1,13 @@ +mod token_channel; +mod types; + +#[cfg(test)] +mod tests; + +pub use self::token_channel::{ + accept_remote_reset, handle_in_move_token, initial_move_token, load_remote_reset_terms, + reset_balance_to_mc_balance, MoveTokenReceived, OutMoveToken, ReceiveMoveTokenOutput, + TokenChannelError, +}; + +pub use types::{TcDbClient, TcOp, TcStatus}; diff --git a/components/funder/src/token_channel/tests/inconsistency_resolve.rs b/components/funder/src/token_channel/tests/inconsistency_resolve.rs new file mode 100644 index 000000000..f58149374 --- /dev/null +++ b/components/funder/src/token_channel/tests/inconsistency_resolve.rs @@ -0,0 +1,227 @@ +use std::collections::HashMap; +use std::convert::TryFrom; + +use futures::task::SpawnExt; +use futures::{future, FutureExt, StreamExt}; + +use common::test_executor::TestExecutor; + +use crypto::hash_lock::HashLock; +use crypto::identity::{Identity, SoftwareEd25519Identity}; +use crypto::rand::RandGen; +use crypto::test_utils::DummyRandom; + +use signature::signature_buff::create_response_signature_buffer; + +use proto::crypto::{ + HashResult, HashedLock, HmacResult, PlainLock, PrivateKey, PublicKey, Signature, Uid, +}; +use proto::funder::messages::{ + Currency, CurrencyOperations, FriendTcOp, RequestSendFundsOp, ResetBalance, ResponseSendFundsOp, +}; + +use identity::{create_identity, IdentityClient}; + +use crate::mutual_credit::incoming::IncomingMessage; +use crate::token_channel::tests::utils::MockTokenChannel; +use crate::token_channel::{ + accept_remote_reset, handle_in_move_token, handle_out_move_token, load_remote_reset_terms, + reset_balance_to_mc_balance, MoveTokenReceived, ReceiveMoveTokenOutput, TcDbClient, TcStatus, + TokenChannelError, +}; +use crate::types::create_pending_transaction; + +async fn task_inconsistency_resolve(test_executor: TestExecutor) { + let currency1 = Currency::try_from("FST1".to_owned()).unwrap(); + let currency2 = Currency::try_from("FST2".to_owned()).unwrap(); + let currency3 = Currency::try_from("FST3".to_owned()).unwrap(); + + let mut rng_a = DummyRandom::new(&[0xau8]); + let pkcs8 = PrivateKey::rand_gen(&mut rng_a); + let identity_a = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); + let pk_a = identity_a.get_public_key(); + + let mut rng_b = DummyRandom::new(&[0xbu8]); + let pkcs8 = PrivateKey::rand_gen(&mut rng_b); + let identity_b = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); + let pk_b = identity_b.get_public_key(); + + let mut tc_a_b = MockTokenChannel::new(&pk_a, &pk_b); + let mut tc_b_a = MockTokenChannel::new(&pk_b, &pk_a); + + // Sort `a` and `b` entities, to have always have `a` as the first sender. + let (pk_a, pk_b, identity_a, identity_b, mut tc_a_b, mut tc_b_a) = + match tc_a_b.get_tc_status().await.unwrap() { + TcStatus::ConsistentOut(..) => (pk_a, pk_b, identity_a, identity_b, tc_a_b, tc_b_a), + TcStatus::ConsistentIn(..) => (pk_b, pk_a, identity_b, identity_a, tc_b_a, tc_a_b), + TcStatus::Inconsistent(..) => unreachable!(), + }; + + // Spawn identity servers: + let (requests_sender_a, identity_server_a) = create_identity(identity_a); + let mut identity_client_a = IdentityClient::new(requests_sender_a); + test_executor + .spawn(identity_server_a.then(|_| future::ready(()))) + .unwrap(); + + let (requests_sender_b, identity_server_b) = create_identity(identity_b); + let mut identity_client_b = IdentityClient::new(requests_sender_b); + test_executor + .spawn(identity_server_b.then(|_| future::ready(()))) + .unwrap(); + + // Send a MoveToken message from b to a, adding a currency: + // -------------------------------------------------------- + let currencies_operations = Vec::new(); + let currencies_diff = vec![currency1.clone()]; + let move_token = handle_out_move_token( + &mut tc_b_a, + &mut identity_client_b, + currencies_operations, + currencies_diff, + &pk_b, + &pk_a, + ) + .await + .unwrap(); + + // Receive the MoveToken message at a: + // ----------------------------------- + assert!(matches!( + handle_in_move_token( + &mut tc_a_b, + &mut identity_client_a, + move_token, + &pk_a, + &pk_b, + ) + .await, + Ok(ReceiveMoveTokenOutput::Received(_)) + )); + + // Assert current counter value: + assert_eq!(tc_a_b.get_move_token_counter().await.unwrap(), 1); + assert_eq!(tc_b_a.get_move_token_counter().await.unwrap(), 1); + + // Send a MoveToken message from a to b, adding two currencies, + // and set incorrect token info hash. + // ------------------------------------------------------------ + let currencies_operations = Vec::new(); + let currencies_diff = vec![currency1.clone(), currency2.clone()]; + let move_token = handle_out_move_token( + &mut tc_a_b, + &mut identity_client_a, + currencies_operations, + currencies_diff, + &pk_a, + &pk_b, + ) + .await + .unwrap(); + + assert_eq!(tc_a_b.get_move_token_counter().await.unwrap(), 2); + assert_eq!(tc_b_a.get_move_token_counter().await.unwrap(), 1); + + // b is reset, this is done to simulate inconsistency: + // --------------------------------------------------- + let mut tc_b_a = MockTokenChannel::new(&pk_b, &pk_a); + assert_eq!(tc_b_a.get_move_token_counter().await.unwrap(), 0); + + // Receive the MoveToken message at b: + // ----------------------------------- + let res = handle_in_move_token( + &mut tc_b_a, + &mut identity_client_b, + move_token, + &pk_b, + &pk_a, + ) + .await + .unwrap(); + + let reset_terms_b = match res { + ReceiveMoveTokenOutput::ChainInconsistent(reset_terms) => { + assert_eq!(reset_terms.move_token_counter, 0 + 2); + assert!(reset_terms.reset_balances.is_empty()); + reset_terms + } + _ => unreachable!(), + }; + + // Set a to be inconsistent, and get a's reset terms: + // ------------------------------------------------- + let reset_terms_a = load_remote_reset_terms( + &mut tc_a_b, + &mut identity_client_a, + reset_terms_b, + &pk_a, + &pk_b, + ) + .await + .unwrap() + .unwrap(); + + // Assert a's reset terms: + assert_eq!(reset_terms_a.move_token_counter, 2 + 2); + assert_eq!(reset_terms_a.reset_balances.len(), 1); + let expected_reset_balance = ResetBalance { + balance: 0, + in_fees: 0.into(), + out_fees: 0.into(), + }; + assert_eq!( + reset_terms_a.reset_balances.get(¤cy1), + Some(&expected_reset_balance) + ); + + // b: load a's reset terms: + // ------------------------ + assert!(matches!( + load_remote_reset_terms( + &mut tc_b_a, + &mut identity_client_b, + reset_terms_a, + &pk_b, + &pk_a, + ) + .await + .unwrap(), + None + )); + + // b accepts a's reset terms: + // -------------------------- + let currencies_operations = Vec::new(); + let currencies_diff = Vec::new(); + let move_token = accept_remote_reset( + &mut tc_b_a, + &mut identity_client_b, + currencies_operations, + currencies_diff, + &pk_b, + &pk_a, + ) + .await + .unwrap(); + + // a: receive reset move token message: + // ----------------------------------- + let res = handle_in_move_token( + &mut tc_a_b, + &mut identity_client_a, + move_token, + &pk_a, + &pk_b, + ) + .await + .unwrap(); + + // TODO: Send an extra move token between a and b, just ot make sure channel is now consistent. +} + +#[test] +fn test_inconsistency_resolve() { + let test_executor = TestExecutor::new(); + let res = test_executor.run(task_inconsistency_resolve(test_executor.clone())); + assert!(res.is_output()); +} diff --git a/components/funder/src/token_channel/tests/mod.rs b/components/funder/src/token_channel/tests/mod.rs new file mode 100644 index 000000000..b3a5012bb --- /dev/null +++ b/components/funder/src/token_channel/tests/mod.rs @@ -0,0 +1,7 @@ +// mod inconsistency_resolve; +mod move_token_basic; +mod utils; + +// TODO: Add more tests +// - Duplicate test +// - Retransmit outgoing test diff --git a/components/funder/src/token_channel/tests/move_token_basic.rs b/components/funder/src/token_channel/tests/move_token_basic.rs new file mode 100644 index 000000000..07fd138b3 --- /dev/null +++ b/components/funder/src/token_channel/tests/move_token_basic.rs @@ -0,0 +1,313 @@ +use std::convert::TryFrom; + +use futures::task::SpawnExt; +use futures::{future, FutureExt, StreamExt}; + +use common::test_executor::TestExecutor; + +use crypto::hash_lock::HashLock; +use crypto::identity::{Identity, SoftwareEd25519Identity}; +use crypto::rand::RandGen; +use crypto::test_utils::DummyRandom; + +use signature::signature_buff::create_response_signature_buffer; + +use proto::crypto::{ + HashResult, HashedLock, HmacResult, PlainLock, PrivateKey, PublicKey, Signature, Uid, +}; +use proto::funder::messages::{Currency, FriendTcOp, RequestSendFundsOp, ResponseSendFundsOp}; + +use identity::{create_identity, IdentityClient}; + +use crate::mutual_credit::incoming::IncomingMessage; +use crate::mutual_credit::utils::mc_response_signature_buffer; +use crate::mutual_credit::{McRequest, McResponse, PendingTransaction}; + +use crate::token_channel::tests::utils::MockTokenChannel; +use crate::token_channel::types::TcCurrencyConfig; +use crate::token_channel::{ + accept_remote_reset, handle_in_move_token, reset_balance_to_mc_balance, MoveTokenReceived, + OutMoveToken, ReceiveMoveTokenOutput, TcDbClient, TcStatus, TokenChannelError, +}; + +async fn task_move_token_basic(test_executor: TestExecutor) { + let currency1 = Currency::try_from("FST1".to_owned()).unwrap(); + let currency2 = Currency::try_from("FST2".to_owned()).unwrap(); + let currency3 = Currency::try_from("FST3".to_owned()).unwrap(); + + let mut rng_a = DummyRandom::new(&[0xau8]); + let pkcs8 = PrivateKey::rand_gen(&mut rng_a); + let identity_a = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); + let pk_a = identity_a.get_public_key(); + + let mut rng_b = DummyRandom::new(&[0xbu8]); + let pkcs8 = PrivateKey::rand_gen(&mut rng_b); + let identity_b = SoftwareEd25519Identity::from_private_key(&pkcs8).unwrap(); + let pk_b = identity_b.get_public_key(); + + let mut tc_a_b = MockTokenChannel::new(&pk_a, &pk_b); + let mut tc_b_a = MockTokenChannel::new(&pk_b, &pk_a); + + // Sort `a` and `b` entities, to have always have `a` as the first sender. + let (pk_a, pk_b, identity_a, identity_b, mut tc_a_b, mut tc_b_a) = + match tc_a_b.get_tc_status().await.unwrap() { + TcStatus::ConsistentOut(..) => (pk_a, pk_b, identity_a, identity_b, tc_a_b, tc_b_a), + TcStatus::ConsistentIn(..) => (pk_b, pk_a, identity_b, identity_a, tc_b_a, tc_a_b), + TcStatus::Inconsistent(..) => unreachable!(), + }; + + // Spawn identity servers: + let (requests_sender_a, identity_server_a) = create_identity(identity_a); + let mut identity_client_a = IdentityClient::new(requests_sender_a); + test_executor + .spawn(identity_server_a.then(|_| future::ready(()))) + .unwrap(); + + let (requests_sender_b, identity_server_b) = create_identity(identity_b); + let mut identity_client_b = IdentityClient::new(requests_sender_b); + test_executor + .spawn(identity_server_b.then(|_| future::ready(()))) + .unwrap(); + + /* + // Send a MoveToken message from b to a, adding a currency: + // -------------------------------------------------------- + // let currencies_operations = Vec::new(); + // let currencies_diff = vec![currency1.clone()]; + + let out_move_token = OutMoveToken::new(); + let move_token = out_move_token + .finalize(&mut tc_b_a, &mut identity_client_b, &pk_b, &pk_a) + .await + .unwrap(); + + // Receive the MoveToken message at a: + // ----------------------------------- + assert!(matches!( + handle_in_move_token( + &mut tc_a_b, + &mut identity_client_a, + move_token, + &pk_a, + &pk_b, + ) + .await, + Ok(ReceiveMoveTokenOutput::Received(_)) + )); + + // Send a MoveToken message from a to b, adding two currencies: + // ------------------------------------------------------------ + let currencies_operations = Vec::new(); + let currencies_diff = vec![currency1.clone(), currency2.clone()]; + let move_token = handle_out_move_token( + &mut tc_a_b, + &mut identity_client_a, + currencies_operations, + currencies_diff, + &pk_a, + &pk_b, + ) + .await + .unwrap(); + + // Receive the MoveToken message at b: + // ----------------------------------- + assert!(matches!( + handle_in_move_token( + &mut tc_b_a, + &mut identity_client_b, + move_token, + &pk_b, + &pk_a, + ) + .await, + Ok(ReceiveMoveTokenOutput::Received(_)) + )); + */ + + // Add currency config at b, to be able to send a request to a: + // --------------------------------------------------------------------------------- + tc_b_a.currency_configs.insert( + currency1.clone(), + TcCurrencyConfig { + local_max_debt: u128::MAX, + remote_max_debt: 0, + }, + ); + + // Send a MoveToken message from b to a, sending a request send funds message: + // --------------------------------------------------------------------------- + let src_plain_lock = PlainLock::from(&[0xaa; PlainLock::len()]); + let mc_request = McRequest { + request_id: Uid::from(&[0; Uid::len()]), + src_hashed_lock: src_plain_lock.clone().hash_lock(), + dest_payment: 20u128, + invoice_hash: HashResult::from(&[0; HashResult::len()]), + route: vec![pk_a.clone()], + left_fees: 5u128, + }; + + let pending_transaction = PendingTransaction::from(mc_request.clone()); + + let mut out_move_token = OutMoveToken::new(); + let _mc_balance = out_move_token + .queue_request(&mut tc_b_a, currency1.clone(), mc_request.clone()) + .await + .unwrap(); + let (move_token, _mentioned_currencies) = out_move_token + .finalize(&mut tc_b_a, &mut identity_client_b, &pk_b, &pk_a) + .await + .unwrap(); + + /* + let move_token = handle_out_move_token( + &mut tc_b_a, + &mut identity_client_b, + currencies_operations, + currencies_diff, + &pk_b, + &pk_a, + ) + .await + .unwrap(); + */ + + { + // Assert balances: + let mut b_balances_iter = tc_b_a.list_balances(); + let mut b_balances = Vec::new(); + while let Some(item) = b_balances_iter.next().await { + b_balances.push(item.unwrap()); + } + assert_eq!(b_balances.len(), 1); + let (currency, mc_balance) = b_balances.pop().unwrap(); + assert_eq!(currency, currency1); + assert_eq!(mc_balance.balance, 0); + assert_eq!(mc_balance.local_pending_debt, 25); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 0.into()); + } + + // Add a remote max debt to a, so that `a` will be able to receive credits from `b`: + // --------------------------------------------------------------------------------- + tc_a_b.currency_configs.insert( + currency1.clone(), + TcCurrencyConfig { + local_max_debt: u128::MAX, + remote_max_debt: 25u128, // 20 credits payment + 5 credits fees + }, + ); + + // Receive the MoveToken message at a: + // ---------------------------------- + let res = handle_in_move_token( + &mut tc_a_b, + &mut identity_client_a, + move_token, + &pk_a, + &pk_b, + ) + .await + .unwrap(); + + // Make sure the the result is as expected: + let mut incoming_messages = match res { + ReceiveMoveTokenOutput::Received(MoveTokenReceived { incoming_messages }) => { + incoming_messages + } + _ => unreachable!(), + }; + + assert_eq!(incoming_messages.len(), 1); + let (currency, incoming_message) = incoming_messages.pop().unwrap(); + assert_eq!(currency, currency1); + let received_mc_request = match incoming_message { + IncomingMessage::Request(received_mc_request) => received_mc_request, + _ => unreachable!(), + }; + assert_eq!(received_mc_request, mc_request); + + // Send a MoveToken message from a to b, sending a response send funds message: + // ---------------------------------------------------------------------------- + let mc_response = { + let mut mc_response = McResponse { + request_id: Uid::from(&[0; Uid::len()]), + src_plain_lock, + serial_num: 0, + // Temporary signature value, calculated later: + signature: Signature::from(&[0; Signature::len()]), + }; + + // Sign the response: + let sign_buffer = + mc_response_signature_buffer(¤cy1, &mc_response, &pending_transaction); + + mc_response.signature = identity_client_a + .request_signature(sign_buffer) + .await + .unwrap(); + mc_response + }; + + let mut out_move_token = OutMoveToken::new(); + let _mc_balance = out_move_token + .queue_response(&mut tc_a_b, currency1.clone(), mc_response, &pk_a) + .await + .unwrap(); + let (move_token, _mentioned_currencies) = out_move_token + .finalize(&mut tc_a_b, &mut identity_client_a, &pk_a, &pk_b) + .await + .unwrap(); + + // Receive the MoveToken message at b: + // ---------------------------------- + let res = handle_in_move_token( + &mut tc_b_a, + &mut identity_client_b, + move_token, + &pk_b, + &pk_a, + ) + .await + .unwrap(); + + { + // Assert balances (b): + let mut b_balances_iter = tc_b_a.list_balances(); + let mut b_balances = Vec::new(); + while let Some(item) = b_balances_iter.next().await { + b_balances.push(item.unwrap()); + } + assert_eq!(b_balances.len(), 1); + let (currency, mc_balance) = b_balances.pop().unwrap(); + assert_eq!(currency, currency1); + assert_eq!(mc_balance.balance, -25); + assert_eq!(mc_balance.local_pending_debt, 0); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 0.into()); + assert_eq!(mc_balance.out_fees, 5.into()); + + // Assert balances (a): + let mut a_balances_iter = tc_a_b.list_balances(); + let mut a_balances = Vec::new(); + while let Some(item) = a_balances_iter.next().await { + a_balances.push(item.unwrap()); + } + assert_eq!(a_balances.len(), 1); + let (currency, mc_balance) = a_balances.pop().unwrap(); + assert_eq!(currency, currency1); + assert_eq!(mc_balance.balance, 25); + assert_eq!(mc_balance.local_pending_debt, 0); + assert_eq!(mc_balance.remote_pending_debt, 0); + assert_eq!(mc_balance.in_fees, 5.into()); + assert_eq!(mc_balance.out_fees, 0.into()); + } +} + +#[test] +fn test_move_token_basic() { + let test_executor = TestExecutor::new(); + let res = test_executor.run(task_move_token_basic(test_executor.clone())); + assert!(res.is_output()); +} diff --git a/components/funder/src/token_channel/tests/utils.rs b/components/funder/src/token_channel/tests/utils.rs new file mode 100644 index 000000000..cd5d32e6f --- /dev/null +++ b/components/funder/src/token_channel/tests/utils.rs @@ -0,0 +1,539 @@ +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; +use std::mem; + +use futures::{future, stream, Future}; + +use common::async_rpc::{AsyncOpResult, AsyncOpStream, OpError}; +use common::conn::BoxFuture; +use common::safe_arithmetic::SafeSignedArithmetic; +use common::u256::U256; + +use proto::crypto::{PublicKey, Signature, Uid}; +use proto::funder::messages::{ + Currency, McBalance, MoveToken, ResetBalance, ResetTerms, TokenInfo, +}; + +use signature::canonical::CanonicalSerialize; + +use database::interface::funder::CurrencyConfig; +use database::transaction::{TransFunc, Transaction}; + +use crypto::hash::hash_buffer; +use crypto::identity::compare_public_key; + +use crate::mutual_credit::tests::MockMutualCredit; +use crate::token_channel::types::{TcCurrencyConfig, TcStatus}; +use crate::token_channel::{initial_move_token, reset_balance_to_mc_balance, TcDbClient}; +use crate::types::{create_hashed, MoveTokenHashed}; + +#[derive(Debug, Clone)] +pub enum MockTcDirection { + In(MoveTokenHashed), + Out(MoveToken, Option), +} + +#[derive(Debug, Clone)] +pub struct TcConsistent { + mutual_credits: HashMap, + direction: MockTcDirection, + move_token_counter: u128, +} + +#[derive(Debug, Clone)] +pub enum MockTcStatus { + Consistent(TcConsistent), + Inconsistent(ResetTerms, Option), +} + +#[derive(Debug, Clone)] +pub struct MockTokenChannel { + status: MockTcStatus, + /// All currency configurations, as seen from the TokenChannel + pub currency_configs: HashMap, +} + +impl MockTokenChannel { + pub fn new(local_public_key: &PublicKey, remote_public_key: &PublicKey) -> Self { + // First move token message for both sides + let move_token_counter = 0; + if compare_public_key(&local_public_key, &remote_public_key) == Ordering::Less { + // We are the first sender + MockTokenChannel { + status: MockTcStatus::Consistent(TcConsistent { + mutual_credits: HashMap::new(), + direction: MockTcDirection::Out( + initial_move_token(local_public_key, remote_public_key), + None, + ), + move_token_counter, + }), + currency_configs: HashMap::new(), + } + } else { + // We are the second sender + let move_token_in = initial_move_token(remote_public_key, local_public_key); + let token_info = TokenInfo { + // No balances yet: + balances_hash: hash_buffer(&[]), + move_token_counter, + }; + + MockTokenChannel { + status: MockTcStatus::Consistent(TcConsistent { + mutual_credits: HashMap::new(), + direction: MockTcDirection::In(create_hashed(&move_token_in, token_info)), + move_token_counter, + }), + currency_configs: HashMap::new(), + } + } + } +} + +/// Calculate ResetBalance for a specific mutual credit +fn calc_reset_balance(mock_token_channel: &MockMutualCredit) -> ResetBalance { + let mc_balance = &mock_token_channel.balance; + + // Calculate in_fees, adding fees from remote pending requests: + let mut in_fees = mc_balance.in_fees; + for (_uid, pending_transaction) in &mock_token_channel.pending_transactions.remote { + in_fees + .checked_add(U256::from(pending_transaction.left_fees)) + .unwrap(); + } + + ResetBalance { + // Calculate reset balance, including pending debt + balance: mc_balance + .balance + .checked_add_unsigned(mc_balance.remote_pending_debt) + .unwrap(), + in_fees, + out_fees: mc_balance.out_fees, + } +} + +impl TcDbClient for MockTokenChannel { + type McDbClient = MockMutualCredit; + + fn mc_db_client(&mut self, currency: Currency) -> AsyncOpResult> { + match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => Box::pin(future::ready(Ok(tc_consistent + .mutual_credits + .get_mut(¤cy)))), + MockTcStatus::Inconsistent(..) => Box::pin(future::ready(Ok(None))), + } + } + + fn get_currency_local_request(&mut self, request_id: Uid) -> AsyncOpResult> { + let tc_consistent = match &self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent, + _ => unreachable!(), + }; + + // Find the first mutual credit that contains this request, and return the corresponding + // currency. + for (currency, mutual_credit) in &tc_consistent.mutual_credits { + if mutual_credit + .pending_transactions + .local + .contains_key(&request_id) + { + return Box::pin(future::ready(Ok(Some(currency.clone())))); + } + } + + // Nothing was found: + Box::pin(future::ready(Ok(None))) + } + + fn get_tc_status(&mut self) -> AsyncOpResult { + let res = Ok(match &self.status { + MockTcStatus::Consistent(tc_consistent) => match &tc_consistent.direction { + MockTcDirection::In(move_token_in) => TcStatus::ConsistentIn(move_token_in.clone()), + MockTcDirection::Out(move_token_out, opt_move_token_in) => { + TcStatus::ConsistentOut(move_token_out.clone(), opt_move_token_in.clone()) + } + }, + MockTcStatus::Inconsistent(local_reset_terms, opt_remote_reset_terms) => { + TcStatus::Inconsistent(local_reset_terms.clone(), opt_remote_reset_terms.clone()) + } + }); + Box::pin(future::ready(res)) + } + + fn set_direction_incoming(&mut self, move_token_hashed: MoveTokenHashed) -> AsyncOpResult<()> { + let tc_consistent = match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent, + _ => unreachable!(), + }; + + tc_consistent.move_token_counter = move_token_hashed.token_info.move_token_counter; + tc_consistent.direction = MockTcDirection::In(move_token_hashed); + Box::pin(future::ready(Ok(()))) + } + + fn set_direction_outgoing( + &mut self, + move_token: MoveToken, + move_token_counter: u128, + ) -> AsyncOpResult<()> { + let tc_consistent = match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent, + _ => unreachable!(), + }; + + // Set `move_token_counter`: + tc_consistent.move_token_counter = move_token_counter; + + let last_move_token_in = match &tc_consistent.direction { + MockTcDirection::In(move_token_in) => move_token_in.clone(), + _ => unreachable!(), + }; + + tc_consistent.direction = MockTcDirection::Out(move_token, Some(last_move_token_in)); + Box::pin(future::ready(Ok(()))) + } + + fn set_direction_outgoing_empty_incoming( + &mut self, + move_token: MoveToken, + move_token_counter: u128, + ) -> AsyncOpResult<()> { + let tc_consistent = match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent, + _ => unreachable!(), + }; + + tc_consistent.direction = MockTcDirection::Out(move_token, None); + Box::pin(future::ready(Ok(()))) + } + + fn set_inconsistent( + &mut self, + local_reset_token: Signature, + local_reset_move_token_counter: u128, + ) -> AsyncOpResult<()> { + // Calculate `reset_balances`: + let reset_balances: HashMap<_, _> = match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent + .mutual_credits + .iter() + .map(|(currency, mock_mutual_credit)| { + (currency.clone(), calc_reset_balance(&mock_mutual_credit)) + }) + .collect(), + MockTcStatus::Inconsistent(..) => unreachable!(), + }; + + // Change status to inconsistent: + self.status = MockTcStatus::Inconsistent( + ResetTerms { + reset_token: local_reset_token, + move_token_counter: local_reset_move_token_counter, + reset_balances, + }, + None, + ); + Box::pin(future::ready(Ok(()))) + } + + /// Set remote terms for reset. Can only be called if we are in inconsistent state. + fn set_inconsistent_remote_terms( + &mut self, + remote_reset_token: Signature, + remote_reset_move_token_counter: u128, + ) -> AsyncOpResult<()> { + let local_reset_terms = match &self.status { + MockTcStatus::Consistent(..) | MockTcStatus::Inconsistent(_, Some(_)) => unreachable!(), + MockTcStatus::Inconsistent(local_reset_terms, None) => local_reset_terms.clone(), + }; + + self.status = MockTcStatus::Inconsistent( + local_reset_terms, + Some(ResetTerms { + reset_token: remote_reset_token, + move_token_counter: remote_reset_move_token_counter, + // Note that reset_balances is currently empty, and needs to filled. + reset_balances: HashMap::new(), + }), + ); + + Box::pin(future::ready(Ok(()))) + } + + fn add_remote_reset_balance( + &mut self, + currency: Currency, + reset_balance: ResetBalance, + ) -> AsyncOpResult<()> { + let remote_reset_terms = match &mut self.status { + MockTcStatus::Consistent(..) | MockTcStatus::Inconsistent(_, None) => unreachable!(), + MockTcStatus::Inconsistent(local_reset_terms, Some(remote_reset_terms)) => { + remote_reset_terms + } + }; + + if let Some(_) = remote_reset_terms + .reset_balances + .insert(currency, reset_balance) + { + unreachable!(); + } + + Box::pin(future::ready(Ok(()))) + } + + /// Simulate outgoing token, to be used before an incoming reset move token (a remote reset) + fn set_outgoing_from_inconsistent(&mut self, move_token: MoveToken) -> AsyncOpResult<()> { + let local_reset_terms = match &self.status { + MockTcStatus::Consistent(..) => unreachable!(), + MockTcStatus::Inconsistent(local_reset_terms, _opt_remote_reset_terms) => { + local_reset_terms.clone() + } + }; + + let mutual_credits = local_reset_terms + .reset_balances + .iter() + .map(|(currency, reset_balance)| { + ( + currency.clone(), + MockMutualCredit::new( + currency.clone(), + reset_balance.balance, + reset_balance.in_fees, + reset_balance.out_fees, + ), + ) + }) + .collect(); + + /* + let currencies_set: HashSet<_> = local_reset_terms + .reset_balances + .iter() + .map(|(currency, _)| currency) + .cloned() + .collect(); + */ + + self.status = MockTcStatus::Consistent(TcConsistent { + mutual_credits, + direction: MockTcDirection::Out(move_token, None), + move_token_counter: local_reset_terms.move_token_counter.checked_sub(1).unwrap(), + }); + + Box::pin(future::ready(Ok(()))) + } + + /// Simulate incoming token, to be used before an outgoing reset move token (a local reset) + fn set_incoming_from_inconsistent( + &mut self, + move_token_hashed: MoveTokenHashed, + ) -> AsyncOpResult<()> { + let remote_reset_terms = match &self.status { + MockTcStatus::Consistent(..) => unreachable!(), + MockTcStatus::Inconsistent(_, None) => unreachable!(), + MockTcStatus::Inconsistent(local_reset_terms, Some(remote_reset_terms)) => { + remote_reset_terms.clone() + } + }; + + let mutual_credits = remote_reset_terms + .reset_balances + .iter() + .map(|(currency, reset_balance)| { + ( + currency.clone(), + MockMutualCredit::new( + currency.clone(), + // Note: direction is flipped: + reset_balance.balance.checked_neg().unwrap(), + reset_balance.out_fees, + reset_balance.in_fees, + ), + ) + }) + .collect(); + + /* + let currencies_set: HashSet<_> = remote_reset_terms + .reset_balances + .iter() + .map(|(currency, _)| currency) + .cloned() + .collect(); + */ + + self.status = MockTcStatus::Consistent(TcConsistent { + mutual_credits, + direction: MockTcDirection::In(move_token_hashed), + move_token_counter: remote_reset_terms + .move_token_counter + .checked_sub(1) + .unwrap(), + }); + + Box::pin(future::ready(Ok(()))) + } + + fn get_move_token_counter(&mut self) -> AsyncOpResult { + Box::pin(future::ready(Ok(match &self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent.move_token_counter, + MockTcStatus::Inconsistent(..) => unreachable!(), + }))) + } + + fn get_currency_config( + &mut self, + currency: Currency, + ) -> AsyncOpResult> { + Box::pin(future::ready(Ok(self + .currency_configs + .get(¤cy) + .cloned()))) + } + + /// Return a sorted async iterator of all balances + fn list_balances(&mut self) -> AsyncOpStream<(Currency, McBalance)> { + let tc_consistent = match &self.status { + MockTcStatus::Consistent(tc_consistent) => tc_consistent, + MockTcStatus::Inconsistent(..) => unreachable!(), + }; + + let mut balances_vec: Vec<_> = tc_consistent + .mutual_credits + .iter() + .map(|(currency, mutual_credit)| (currency.clone(), mutual_credit.balance.clone())) + .collect(); + + balances_vec.sort_by(|(currency1, _), (currency2, _)| currency1.cmp(currency2)); + Box::pin(stream::iter(balances_vec.into_iter().map(|item| Ok(item)))) + } + + /// Return a sorted async iterator of all local reset proposal balances + /// Only relevant for inconsistent channels + fn list_local_reset_balances(&mut self) -> AsyncOpStream<(Currency, ResetBalance)> { + let local_reset_terms = match &self.status { + MockTcStatus::Consistent(..) => unreachable!(), + MockTcStatus::Inconsistent(local_reset_terms, _opt_remote_reset_terms) => { + local_reset_terms + } + }; + + let mut balances_vec: Vec<_> = local_reset_terms + .reset_balances + .iter() + .map(|(currency, reset_balance)| (currency.clone(), reset_balance.clone())) + .collect(); + + balances_vec.sort_by(|(currency1, _), (currency2, _)| currency1.cmp(currency2)); + Box::pin(stream::iter(balances_vec.into_iter().map(|item| Ok(item)))) + } + + /// Return a sorted async iterator of all remote reset proposal balances + /// Only relevant for inconsistent channels + fn list_remote_reset_balances(&mut self) -> AsyncOpStream<(Currency, ResetBalance)> { + let remote_reset_terms = match &self.status { + MockTcStatus::Consistent(..) | MockTcStatus::Inconsistent(_, None) => unreachable!(), + MockTcStatus::Inconsistent(_local_reset_terms, Some(remote_reset_terms)) => { + remote_reset_terms + } + }; + + let mut balances_vec: Vec<_> = remote_reset_terms + .reset_balances + .iter() + .map(|(currency, reset_balance)| (currency.clone(), reset_balance.clone())) + .collect(); + + balances_vec.sort_by(|(currency1, _), (currency2, _)| currency1.cmp(currency2)); + Box::pin(stream::iter(balances_vec.into_iter().map(|item| Ok(item)))) + } + + /* + fn add_mutual_credit(&mut self, currency: Currency) -> AsyncOpResult<()> { + Box::pin(future::ready(Ok(match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => { + tc_consistent.local_currencies.insert(currency) + } + MockTcStatus::Inconsistent(..) => unreachable!(), + }))) + } + */ + + /* + fn remove_local_currency(&mut self, currency: Currency) -> AsyncOpResult { + Box::pin(future::ready(Ok(match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => { + tc_consistent.local_currencies.remove(¤cy) + } + MockTcStatus::Inconsistent(..) => unreachable!(), + }))) + } + + fn add_remote_currency(&mut self, currency: Currency) -> AsyncOpResult { + Box::pin(future::ready(Ok(match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => { + tc_consistent.remote_currencies.insert(currency) + } + MockTcStatus::Inconsistent(..) => unreachable!(), + }))) + } + + fn remove_remote_currency(&mut self, currency: Currency) -> AsyncOpResult { + Box::pin(future::ready(Ok(match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => { + tc_consistent.remote_currencies.remove(¤cy) + } + MockTcStatus::Inconsistent(..) => unreachable!(), + }))) + } + */ + + fn add_mutual_credit(&mut self, currency: Currency) -> AsyncOpResult<()> { + let balance = 0; + let in_fees = 0.into(); + let out_fees = 0.into(); + match &mut self.status { + MockTcStatus::Consistent(tc_consistent) => { + let res = tc_consistent.mutual_credits.insert( + currency.clone(), + MockMutualCredit::new(currency, balance, in_fees, out_fees), + ); + if let Some(_) = res { + unreachable!(); + } + } + MockTcStatus::Inconsistent(..) => unreachable!(), + }; + Box::pin(future::ready(Ok(()))) + } +} + +impl Transaction for MockTokenChannel { + fn transaction<'a, F, T, E>(&'a mut self, f: F) -> BoxFuture<'a, Result> + where + F: TransFunc> + Send + 'a, + T: Send, + E: Send, + { + Box::pin(async move { + // Save original value, before we start making modifications: + let orig_mock_token_channel = self.clone(); + match f.call(self).await { + Ok(t) => { + // Transaction was successful + Ok(t) + } + Err(e) => { + // Cancel transaction + let _ = mem::replace(self, orig_mock_token_channel); + Err(e) + } + } + }) + } +} diff --git a/components/funder/src/token_channel/token_channel.rs b/components/funder/src/token_channel/token_channel.rs new file mode 100644 index 000000000..5941487df --- /dev/null +++ b/components/funder/src/token_channel/token_channel.rs @@ -0,0 +1,1108 @@ +use std::cmp::Ordering; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::fmt::Debug; +use std::marker::PhantomData; + +use derive_more::From; + +use futures::{Stream, StreamExt, TryStreamExt}; + +use common::async_rpc::OpError; +use common::conn::BoxFuture; + +use crypto::hash::{hash_buffer, Hasher}; +use crypto::identity::compare_public_key; + +use identity::IdentityClient; + +use proto::app_server::messages::RelayAddress; +use proto::crypto::{HashResult, PublicKey, RandValue, Signature}; +use proto::funder::messages::{ + CancelSendFundsOp, Currency, FriendTcOp, McBalance, MoveToken, RequestSendFundsOp, + ResetBalance, ResetTerms, ResponseSendFundsOp, TokenInfo, +}; + +use signature::canonical::CanonicalSerialize; +use signature::signature_buff::{ + hash_token_info, move_token_signature_buff, reset_token_signature_buff, +}; +use signature::verify::verify_move_token; + +use database::transaction::{TransFunc, Transaction}; + +use crate::mutual_credit::incoming::{ + process_operation, IncomingMessage, ProcessOperationError, ProcessOutput, +}; +use crate::mutual_credit::outgoing::{ + queue_cancel, queue_request, queue_response, QueueOperationError, +}; +use crate::mutual_credit::{McCancel, McDbClient, McOp, McRequest, McResponse}; + +use crate::token_channel::types::{TcDbClient, TcOp, TcStatus}; +use crate::types::{create_hashed, MoveTokenHashed}; + +/// Unrecoverable TokenChannel error +#[derive(Debug, From)] +pub enum TokenChannelError { + InvalidTransaction(ProcessOperationError), + MoveTokenCounterOverflow, + RequestSignatureError, + InvalidState, + OpError(OpError), + QueueOperationError(QueueOperationError), +} + +#[derive(Debug)] +pub struct MoveTokenReceived { + pub incoming_messages: Vec<(Currency, IncomingMessage)>, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +pub enum ReceiveMoveTokenOutput { + Duplicate, + RetransmitOutgoing(MoveToken), + Received(MoveTokenReceived), + ChainInconsistent(ResetTerms), +} + +/// Create a token from a public key +/// Currently this function puts the public key in the beginning of the signature buffer, +/// as the public key is shorter than a signature. +/// Possibly change this in the future (Maybe use a hash to spread the public key over all the +/// bytes of the signature) +/// +/// Note that the output here is not a real signature. This function is used for the first +/// deterministic initialization of a token channel. +fn token_from_public_key(public_key: &PublicKey) -> Signature { + let mut buff = [0; Signature::len()]; + buff[0..PublicKey::len()].copy_from_slice(public_key); + Signature::from(&buff) +} + +/// Create an initial move token in the relationship between two public keys. +/// To canonicalize the initial move token (Having an equal move token for both sides), we sort the +/// two public keys in some way. +pub fn initial_move_token(low_public_key: &PublicKey, high_public_key: &PublicKey) -> MoveToken { + /* + let token_info = TokenInfo { + // No balances yet: + balances_hash: hash_buffer(&[]), + move_token_counter: 0, + }; + + let info_hash = hash_token_info(&low_public_key, &high_public_key, &token_info); + */ + + // This is a special initialization case. + // Note that this is the only case where new_token is not a valid signature. + // We do this because we want to have synchronization between the two sides of the token + // channel, however, the remote side has no means of generating the signature (Because he + // doesn't have the private key). Therefore we use a dummy new_token instead. + let move_token_out = MoveToken { + old_token: token_from_public_key(&low_public_key), + operations: Vec::new(), + new_token: token_from_public_key(&high_public_key), + }; + + move_token_out +} + +/* +/// Create a new token channel, and determines the initial state for the local side. +/// The initial state is determined according to the order between the two provided public keys. +/// Note that this function does not affect the database, it only returns a new state for the token +/// channel. +pub async fn init_token_channel( + tc_client: &mut impl TcClient, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result<(), TokenChannelError> +where + B: CanonicalSerialize + Clone, +{ + let move_token_counter = 0; + Ok( + if compare_public_key(&local_public_key, &remote_public_key) == Ordering::Less { + // We are the first sender + tc_client + .set_direction_outgoing_empty_incoming( + initial_move_token(local_public_key, remote_public_key), + move_token_counter, + ) + .await? + } else { + // We are the second sender + let move_token_in = initial_move_token(remote_public_key, local_public_key); + let token_info = TokenInfo { + // No balances yet: + balances_hash: hash_buffer(&[]), + move_token_counter, + }; + tc_client + .set_direction_incoming(create_hashed::(&move_token_in, &token_info)) + .await? + }, + ) +} +*/ + +// TODO: Possibly have this function as a method on the ResetBalance struct. +/// Create a new McBalance (with zero pending fees) based on a given ResetBalance +pub fn reset_balance_to_mc_balance(reset_balance: ResetBalance) -> McBalance { + McBalance { + balance: reset_balance.balance, + local_pending_debt: 0, + remote_pending_debt: 0, + in_fees: reset_balance.in_fees, + out_fees: reset_balance.out_fees, + } +} + +/// Extract all local balances for reset as a map: +async fn local_balances_for_reset( + tc_client: &mut impl TcDbClient, +) -> Result, TokenChannelError> { + let mut balances = HashMap::new(); + let mut reset_balances = tc_client.list_local_reset_balances(); + while let Some(item) = reset_balances.next().await { + let (currency, reset_balance) = item?; + if let Some(_) = balances.insert(currency, reset_balance) { + return Err(TokenChannelError::InvalidState); + } + } + Ok(balances) +} + +struct InconsistentTrans<'a, C> { + input_phantom: PhantomData, + move_token_out: MoveToken, + new_move_token: MoveToken, + local_public_key: &'a PublicKey, + remote_public_key: &'a PublicKey, +} + +enum TransactFail { + TokenChannelError(TokenChannelError), + InvalidIncoming(InvalidIncoming), +} + +impl<'b, C> TransFunc for InconsistentTrans<'b, C> +where + C: TcDbClient + Send, + C::McDbClient: Send, +{ + type InRef = C; + // We divide the error into two types: + // Recoverable: InvalidIncoming + // Unrecoverable: TokenChannelError + type Out = Result>; + + fn call<'a>(self, tc_client: &'a mut Self::InRef) -> BoxFuture<'a, Self::Out> + where + Self: 'a, + { + Box::pin(async move { + let incoming_token_match_output = async move { + tc_client + .set_outgoing_from_inconsistent(self.move_token_out.clone()) + .await?; + + // Attempt to receive an incoming token: + handle_incoming_token_match( + tc_client, + self.new_move_token, + self.local_public_key, + self.remote_public_key, + ) + .await + } + .await + .map_err(|e| Err(e))?; + + match incoming_token_match_output { + IncomingTokenMatchOutput::MoveTokenReceived(move_token_received) => { + Ok(move_token_received) + } + IncomingTokenMatchOutput::InvalidIncoming(invalid_incoming) => { + Err(Ok(invalid_incoming)) + } + } + }) + } +} + +pub async fn handle_in_move_token( + tc_client: &mut C, + identity_client: &mut IdentityClient, + new_move_token: MoveToken, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result +where + C: TcDbClient + Transaction + Send, + C::McDbClient: Send, +{ + match tc_client.get_tc_status().await? { + TcStatus::ConsistentIn(move_token_in) => { + handle_in_move_token_dir_in( + tc_client, + identity_client, + local_public_key, + remote_public_key, + move_token_in, + new_move_token, + ) + .await + } + TcStatus::ConsistentOut(move_token_out, _opt_move_token_in) => { + handle_in_move_token_dir_out( + tc_client, + identity_client, + move_token_out, + new_move_token, + local_public_key, + remote_public_key, + ) + .await + } + TcStatus::Inconsistent(local_reset_terms, _opt_remote_reset_terms) => { + // Might be a reset move token + if new_move_token.old_token == local_reset_terms.reset_token { + // This is a reset move token! + + let move_token_out = MoveToken { + old_token: Signature::from(&[0; Signature::len()]), + operations: Vec::new(), + new_token: local_reset_terms.reset_token.clone(), + }; + + // Atomically attempt to handle a reset move token: + let output = tc_client + .transaction(InconsistentTrans { + input_phantom: PhantomData::, + move_token_out, + new_move_token, + local_public_key, + remote_public_key, + }) + .await; + + match output { + Ok(move_token_received) => { + Ok(ReceiveMoveTokenOutput::Received(move_token_received)) + } + Err(e) => { + let invalid_incoming = e?; + // In this case the transaction was not committed: + Ok(ReceiveMoveTokenOutput::ChainInconsistent(ResetTerms { + reset_token: local_reset_terms.reset_token, + move_token_counter: local_reset_terms.move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + })) + } + } + } else { + Ok(ReceiveMoveTokenOutput::ChainInconsistent(ResetTerms { + reset_token: local_reset_terms.reset_token, + move_token_counter: local_reset_terms.move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + })) + } + } + } +} + +// Sign over a blob that contains: +// - hash(prefix ("SOME_STRING")) +// - Both public keys. +// - local_reset_move_token_counter +/// Generate a reset token, to be used by remote side if he wants to accept the reset terms. +async fn create_reset_token( + identity_client: &mut IdentityClient, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, + move_token_counter: u128, +) -> Result { + let sign_buffer = + reset_token_signature_buff(local_public_key, remote_public_key, move_token_counter); + Ok(identity_client + .request_signature(sign_buffer) + .await + .map_err(|_| TokenChannelError::RequestSignatureError)?) +} + +/// Set token channel to be inconsistent +async fn set_inconsistent( + tc_client: &mut impl TcDbClient, + identity_client: &mut IdentityClient, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result<(Signature, u128), TokenChannelError> { + // We increase by 2, because the other side might have already signed a move token that equals + // to our move token plus 1, so he might not be willing to sign another move token with the + // same `move_token_counter`. There could be a gap of size `1` as a result, but we don't care + // about it, as long as the `move_token_counter` values are monotonic. + let local_reset_move_token_counter = tc_client + .get_move_token_counter() + .await? + .checked_add(2) + .ok_or(TokenChannelError::MoveTokenCounterOverflow)?; + + // Create a local reset token, to be used by the remote side to accept our terms: + let local_reset_token = create_reset_token( + identity_client, + local_public_key, + remote_public_key, + local_reset_move_token_counter, + ) + .await?; + + let balances = { + let mut balances = HashMap::::new(); + let mut balances_stream = tc_client.list_balances(); + while let Some(res) = balances_stream.next().await { + let (currency, mc_balance) = res?; + balances.insert(currency, mc_balance); + } + balances + }; + + tc_client + .set_inconsistent_local_terms( + local_reset_token.clone(), + local_reset_move_token_counter.clone(), + ) + .await?; + + for (currency, mc_balance) in balances { + let reset_balance = ResetBalance { + balance: todo!(), + // TODO: How to calculate in_fees/out_fees? We need to take into account current + // pending requests. + in_fees: todo!(), + out_fees: todo!(), + }; + tc_client + .add_local_reset_balance(currency, reset_balance) + .await?; + } + + Ok((local_reset_token, local_reset_move_token_counter)) +} + +async fn handle_in_move_token_dir_in( + tc_client: &mut impl TcDbClient, + identity_client: &mut IdentityClient, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, + move_token_in: MoveTokenHashed, + new_move_token: MoveToken, +) -> Result { + if move_token_in == create_hashed(&new_move_token, move_token_in.token_info.clone()) { + // Duplicate + Ok(ReceiveMoveTokenOutput::Duplicate) + } else { + // Inconsistency + let (local_reset_token, local_reset_move_token_counter) = set_inconsistent( + tc_client, + identity_client, + local_public_key, + remote_public_key, + ) + .await?; + + Ok(ReceiveMoveTokenOutput::ChainInconsistent(ResetTerms { + reset_token: local_reset_token, + move_token_counter: local_reset_move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + })) + } +} + +struct InMoveTokenDirOutTrans<'a, C> { + input_phantom: PhantomData, + new_move_token: MoveToken, + local_public_key: &'a PublicKey, + remote_public_key: &'a PublicKey, +} + +impl<'b, C> TransFunc for InMoveTokenDirOutTrans<'b, C> +where + C: TcDbClient + Send, + C::McDbClient: Send, +{ + type InRef = C; + // We divide the error into two types: + // Recoverable: InvalidIncoming + // Unrecoverable: TokenChannelError + type Out = Result>; + + fn call<'a>(self, tc_client: &'a mut Self::InRef) -> BoxFuture<'a, Self::Out> + where + Self: 'a, + { + Box::pin(async move { + let incoming_token_match_output = async move { + // Attempt to receive an incoming token: + handle_incoming_token_match( + tc_client, + self.new_move_token, + self.local_public_key, + self.remote_public_key, + ) + .await + } + .await + .map_err(|e| Err(e))?; + + match incoming_token_match_output { + IncomingTokenMatchOutput::MoveTokenReceived(move_token_received) => { + Ok(move_token_received) + } + IncomingTokenMatchOutput::InvalidIncoming(invalid_incoming) => { + Err(Ok(invalid_incoming)) + } + } + }) + } +} + +async fn handle_in_move_token_dir_out( + tc_client: &mut C, + identity_client: &mut IdentityClient, + move_token_out: MoveToken, + new_move_token: MoveToken, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result +where + C: TcDbClient + Transaction + Send, + C::McDbClient: Send, +{ + if new_move_token.old_token == move_token_out.new_token { + // Atomically attempt to handle a reset move token: + let output = tc_client + .transaction(InMoveTokenDirOutTrans { + input_phantom: PhantomData::, + new_move_token, + local_public_key, + remote_public_key, + }) + .await; + + match output { + Ok(move_token_received) => Ok(ReceiveMoveTokenOutput::Received(move_token_received)), + Err(e) => { + let invalid_incoming = e?; + // Inconsistency + // In this case the transaction was not committed: + let (local_reset_token, local_reset_move_token_counter) = set_inconsistent( + tc_client, + identity_client, + local_public_key, + remote_public_key, + ) + .await?; + Ok(ReceiveMoveTokenOutput::ChainInconsistent(ResetTerms { + reset_token: local_reset_token, + move_token_counter: local_reset_move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + })) + } + } + + // self.outgoing_to_incoming(friend_move_token, new_move_token) + } else if move_token_out.old_token == new_move_token.new_token { + // We should retransmit our move token message to the remote side. + Ok(ReceiveMoveTokenOutput::RetransmitOutgoing( + move_token_out.clone(), + )) + } else { + // Inconsistency + let (local_reset_token, local_reset_move_token_counter) = set_inconsistent( + tc_client, + identity_client, + local_public_key, + remote_public_key, + ) + .await?; + Ok(ReceiveMoveTokenOutput::ChainInconsistent(ResetTerms { + reset_token: local_reset_token, + move_token_counter: local_reset_move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + })) + } +} + +// TODO: Should we move this function to offset-signature crate? +async fn hash_mc_infos( + mut mc_infos: impl Stream> + Unpin, +) -> Result { + let mut hasher = Hasher::new(); + // Last seen currency, used to verify sorted order: + let mut opt_last_currency: Option = None; + + while let Some(item) = mc_infos.next().await { + let (currency, mc_balance) = item?; + // Ensure that the list is sorted: + if let Some(last_currency) = opt_last_currency { + assert!(last_currency <= currency); + } + // Update the last currency: + opt_last_currency = Some(currency.clone()); + hasher.update(&hash_buffer(¤cy.canonical_serialize())); + hasher.update(&mc_balance.canonical_serialize()); + } + Ok(hasher.finalize()) +} + +#[derive(Debug)] +enum InvalidIncoming { + InvalidSignature, + InvalidOperation, +} + +#[derive(Debug)] +enum IncomingTokenMatchOutput { + MoveTokenReceived(MoveTokenReceived), + InvalidIncoming(InvalidIncoming), +} + +async fn handle_incoming_token_match( + tc_client: &mut impl TcDbClient, + new_move_token: MoveToken, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result { + // Verify signature: + // Note that we only verify the signature here, and not at the Incoming part. + // This allows the genesis move token to occur smoothly, even though its signature + // is not correct. + // TODO: Check if the above statement is still true. + + // Aggregate incoming messages: + let mut move_token_received = MoveTokenReceived { + incoming_messages: Vec::new(), + }; + + // Attempt to apply operations for every currency: + for friend_tc_op in new_move_token.operations.iter().cloned() { + let tc_op = + if let Some(tc_op) = tc_op_from_incoming_friend_tc_op(tc_client, friend_tc_op).await? { + tc_op + } else { + return Ok(IncomingTokenMatchOutput::InvalidIncoming( + InvalidIncoming::InvalidOperation, + )); + }; + + let remote_max_debt = if let Some(remote_max_debt) = tc_client + .get_currency_config(tc_op.currency.clone()) + .await? + .map(|currency_config| currency_config.remote_max_debt) + { + remote_max_debt + } else { + // Currency is not configured. + match tc_op.mc_op { + McOp::Request(mc_request) => { + // In case of a request, we cancel it: + move_token_received.incoming_messages.push(( + tc_op.currency.clone(), + IncomingMessage::RequestCancel(mc_request), + )); + continue; + } + McOp::Response(..) | McOp::Cancel(..) => { + return Ok(IncomingTokenMatchOutput::InvalidIncoming( + InvalidIncoming::InvalidOperation, + )); + } + } + }; + + let mc_client = + if let Some(mc_client) = tc_client.mc_db_client(tc_op.currency.clone()).await? { + mc_client + } else { + match &tc_op.mc_op { + McOp::Request(..) => { + // An incoming request might allow opening a mutual credit, in case we have a matching + // currency configuration. Otherwise, the request should be cancelled. + tc_client.add_mutual_credit(tc_op.currency.clone()).await?; + tc_client + .mc_db_client(tc_op.currency.clone()) + .await? + .ok_or(TokenChannelError::InvalidState)? + } + McOp::Response(..) | McOp::Cancel(..) => { + return Ok(IncomingTokenMatchOutput::InvalidIncoming( + InvalidIncoming::InvalidOperation, + )); + } + } + }; + + let process_output = process_operation( + mc_client, + tc_op.mc_op, + &tc_op.currency, + remote_public_key, + remote_max_debt, + ) + .await?; + + let incoming_message = match process_output { + ProcessOutput::Incoming(incoming_message) => incoming_message, + ProcessOutput::Inconsistency => { + // TODO: Possibly rename returned error here? + return Ok(IncomingTokenMatchOutput::InvalidIncoming( + InvalidIncoming::InvalidOperation, + )); + } + }; + + move_token_received + .incoming_messages + .push((tc_op.currency.clone(), incoming_message)); + } + + let move_token_counter = tc_client.get_move_token_counter().await?; + let new_move_token_counter = move_token_counter + .checked_add(1) + .ok_or(TokenChannelError::MoveTokenCounterOverflow)?; + + // Calculate `info_hash` as seen by the remote side. + // Create what we expect to be TokenInfo (From the point of view of remote side): + let mc_balances = tc_client.list_balances(); + + let token_info = TokenInfo { + balances_hash: hash_mc_infos( + mc_balances.map_ok(|(currency, mc_balance)| (currency, mc_balance.flip())), + ) + .await?, + move_token_counter: new_move_token_counter, + }; + + let info_hash = hash_token_info(remote_public_key, local_public_key, &token_info); + + // Verify signature: + if !verify_move_token(&new_move_token, &info_hash, &remote_public_key) { + return Ok(IncomingTokenMatchOutput::InvalidIncoming( + InvalidIncoming::InvalidSignature, + )); + } + + // Set direction to outgoing, with the newly received move token: + let new_move_token_hashed = create_hashed(&new_move_token, token_info); + tc_client + .set_direction_incoming(new_move_token_hashed) + .await?; + + Ok(IncomingTokenMatchOutput::MoveTokenReceived( + move_token_received, + )) +} + +async fn tc_op_from_incoming_friend_tc_op( + tc_client: &mut impl TcDbClient, + friend_tc_op: FriendTcOp, +) -> Result, TokenChannelError> { + Ok(Some(match friend_tc_op { + FriendTcOp::RequestSendFunds(request_send_funds) => TcOp { + currency: request_send_funds.currency, + mc_op: McOp::Request(McRequest { + request_id: request_send_funds.request_id, + src_hashed_lock: request_send_funds.src_hashed_lock, + dest_payment: request_send_funds.dest_payment, + invoice_hash: request_send_funds.invoice_hash, + route: request_send_funds.route, + left_fees: request_send_funds.left_fees, + }), + }, + FriendTcOp::ResponseSendFunds(response_send_funds) => { + let currency = if let Some(currency) = tc_client + .get_currency_local_request(response_send_funds.request_id.clone()) + .await? + { + currency + } else { + return Ok(None); + }; + + TcOp { + currency, + mc_op: McOp::Response(McResponse { + request_id: response_send_funds.request_id, + src_plain_lock: response_send_funds.src_plain_lock, + serial_num: response_send_funds.serial_num, + signature: response_send_funds.signature, + }), + } + } + FriendTcOp::CancelSendFunds(cancel_send_funds) => { + let currency = if let Some(currency) = tc_client + .get_currency_local_request(cancel_send_funds.request_id.clone()) + .await? + { + currency + } else { + return Ok(None); + }; + + TcOp { + currency, + mc_op: McOp::Cancel(McCancel { + request_id: cancel_send_funds.request_id, + }), + } + } + })) +} + +fn friend_tc_op_from_outgoing_tc_op(tc_op: TcOp) -> FriendTcOp { + match tc_op.mc_op { + McOp::Request(mc_request) => FriendTcOp::RequestSendFunds(RequestSendFundsOp { + request_id: mc_request.request_id, + currency: tc_op.currency, + src_hashed_lock: mc_request.src_hashed_lock, + dest_payment: mc_request.dest_payment, + invoice_hash: mc_request.invoice_hash, + route: mc_request.route, + left_fees: mc_request.left_fees, + }), + McOp::Response(mc_response) => FriendTcOp::ResponseSendFunds(ResponseSendFundsOp { + request_id: mc_response.request_id, + src_plain_lock: mc_response.src_plain_lock, + serial_num: mc_response.serial_num, + signature: mc_response.signature, + }), + McOp::Cancel(mc_cancel) => FriendTcOp::CancelSendFunds(CancelSendFundsOp { + request_id: mc_cancel.request_id, + }), + } +} + +fn mentioned_currencies(tc_ops: &[TcOp]) -> Vec { + let mentioned_set: HashSet = tc_ops + .iter() + .map(|tc_op| &tc_op.currency) + .cloned() + .collect(); + + let mut mentioned_vec: Vec = mentioned_set.into_iter().collect(); + + // We sort the mentioned currencies to have deterministic output: + mentioned_vec.sort(); + + mentioned_vec +} + +pub struct OutMoveToken { + tc_ops: Vec, +} + +impl OutMoveToken { + pub fn new() -> Self { + Self { tc_ops: Vec::new() } + } + + pub fn len(&self) -> usize { + self.tc_ops.len() + } + + pub fn is_empty(&self) -> bool { + self.tc_ops.is_empty() + } + + pub async fn queue_request( + &mut self, + tc_client: &mut impl TcDbClient, + currency: Currency, + mc_request: McRequest, + ) -> Result, TokenChannelError> { + // Token channel must be at a consistent incoming state + assert!(matches!( + tc_client.get_tc_status().await?, + TcStatus::ConsistentIn(..) + )); + + let local_max_debt = tc_client + .get_currency_config(currency.clone()) + .await? + .ok_or(TokenChannelError::InvalidState)? + .local_max_debt; + let mc_client = if let Some(mc_client) = tc_client.mc_db_client(currency.clone()).await? { + mc_client + } else { + // We need to create a new mutual credit + tc_client.add_mutual_credit(currency.clone()).await?; + tc_client + .mc_db_client(currency.clone()) + .await? + .ok_or(TokenChannelError::InvalidState)? + }; + + // queue_request might fail due to `local_max_debt`. + if let Err(mc_cancel) = + queue_request(mc_client, mc_request.clone(), ¤cy, local_max_debt).await? + { + return Ok(Err(mc_cancel)); + } + + self.tc_ops.push(TcOp { + currency: currency.clone(), + mc_op: McOp::Request(mc_request), + }); + + // Get resulting balances (After queue-ing the request): + Ok(Ok(mc_client.get_balance().await?)) + } + + pub async fn queue_response( + &mut self, + tc_client: &mut impl TcDbClient, + currency: Currency, + mc_response: McResponse, + local_public_key: &PublicKey, + ) -> Result { + // Token channel must be at a consistent incoming state + assert!(matches!( + tc_client.get_tc_status().await?, + TcStatus::ConsistentIn(..) + )); + + let mc_client = tc_client + .mc_db_client(currency.clone()) + .await? + .ok_or(TokenChannelError::InvalidState)?; + + queue_response(mc_client, mc_response.clone(), ¤cy, local_public_key).await?; + + self.tc_ops.push(TcOp { + currency, + mc_op: McOp::Response(mc_response), + }); + + Ok(mc_client.get_balance().await?) + } + + pub async fn queue_cancel( + &mut self, + tc_client: &mut impl TcDbClient, + currency: Currency, + mc_cancel: McCancel, + ) -> Result { + // Token channel must be at a consistent incoming state + assert!(matches!( + tc_client.get_tc_status().await?, + TcStatus::ConsistentIn(..) + )); + + let mc_client = tc_client + .mc_db_client(currency.clone()) + .await? + .ok_or(TokenChannelError::InvalidState)?; + queue_cancel(mc_client, mc_cancel.clone()).await?; + + self.tc_ops.push(TcOp { + currency, + mc_op: McOp::Cancel(mc_cancel), + }); + + Ok(mc_client.get_balance().await?) + } + + // TODO: Possibly rename to "send" or something related? + /// Create MoveToken, and apply to token channel + pub async fn finalize( + self, + tc_client: &mut impl TcDbClient, + identity_client: &mut IdentityClient, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, + ) -> Result<(MoveToken, Vec), TokenChannelError> { + // Token channel must be at a consistent incoming state + assert!(matches!( + tc_client.get_tc_status().await?, + TcStatus::ConsistentIn(..) + )); + + let move_token_in = match tc_client.get_tc_status().await? { + TcStatus::ConsistentIn(move_token_in) => move_token_in, + TcStatus::ConsistentOut(..) | TcStatus::Inconsistent(..) => { + return Err(TokenChannelError::InvalidState); + } + }; + + let new_move_token_counter = tc_client + .get_move_token_counter() + .await? + .checked_add(1) + .ok_or(TokenChannelError::MoveTokenCounterOverflow)?; + let mc_balances = tc_client.list_balances(); + + let token_info = TokenInfo { + balances_hash: hash_mc_infos(mc_balances).await?, + move_token_counter: new_move_token_counter, + }; + + let info_hash = hash_token_info(local_public_key, remote_public_key, &token_info); + + // Get all mentioned currencies in this move token message: + let mentioned_currencies = mentioned_currencies(&self.tc_ops[..]); + + let mut move_token = MoveToken { + old_token: move_token_in.new_token, + operations: self + .tc_ops + .into_iter() + .map(|tc_op| friend_tc_op_from_outgoing_tc_op(tc_op)) + .collect(), + // Still not known: + new_token: Signature::from(&[0; Signature::len()]), + }; + + // Fill in signature: + let signature_buff = move_token_signature_buff(&move_token, &info_hash); + move_token.new_token = identity_client + .request_signature(signature_buff) + .await + .map_err(|_| TokenChannelError::RequestSignatureError)?; + + // Set the direction to be outgoing, and save last incoming token in an atomic way: + tc_client + .set_direction_outgoing(move_token.clone(), new_move_token_counter) + .await?; + + Ok((move_token, mentioned_currencies)) + } +} + +/// Apply a token channel reset, accepting remote side's +/// reset terms. +pub async fn accept_remote_reset( + tc_client: &mut impl TcDbClient, + identity_client: &mut IdentityClient, + operations: Vec, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result<(MoveToken, Vec), TokenChannelError> { + // Make sure that we are in an inconsistent state, + // and that the remote side has already sent his reset terms: + let (remote_reset_token, remote_reset_move_token_counter) = + match tc_client.get_tc_status().await? { + TcStatus::ConsistentIn(..) + | TcStatus::ConsistentOut(..) + | TcStatus::Inconsistent(_, None) => { + // We don't have the remote side's reset terms yet: + return Err(TokenChannelError::InvalidState); + } + TcStatus::Inconsistent(_local_reset_terms, Some(remote_reset_terms)) => ( + remote_reset_terms.reset_token, + remote_reset_terms.move_token_counter, + ), + }; + + // Simulate an incoming move token with the correct `new_token`: + let token_info = TokenInfo { + balances_hash: hash_mc_infos(tc_client.list_remote_reset_balances().map_ok( + |(currency, reset_balance)| (currency, reset_balance_to_mc_balance(reset_balance)), + )) + .await?, + move_token_counter: remote_reset_move_token_counter + .checked_sub(1) + // We check in `load_remote_reset_terms()` that the proposed `reset_move_token_counter` + // is nonzero. + .ok_or(TokenChannelError::InvalidState)?, + }; + + let move_token_in = MoveToken { + old_token: Signature::from(&[0; Signature::len()]), + operations: Vec::new(), + new_token: remote_reset_token.clone(), + }; + + tc_client + .set_incoming_from_inconsistent(create_hashed(&move_token_in, token_info)) + .await?; + + // Create an outgoing move token, to be sent to the remote side: + OutMoveToken::new() + .finalize( + tc_client, + identity_client, + local_public_key, + remote_public_key, + ) + .await +} + +/// Load the remote reset terms information from a remote side's inconsistency message +/// Optionally returns local reset terms (If not already inconsistent) +pub async fn load_remote_reset_terms( + tc_client: &mut impl TcDbClient, + identity_client: &mut IdentityClient, + remote_reset_terms: ResetTerms, + local_public_key: &PublicKey, + remote_public_key: &PublicKey, +) -> Result, TokenChannelError> { + // Check our current state: + let (opt_local_reset_terms, last_move_token_counter) = match tc_client.get_tc_status().await? { + TcStatus::ConsistentIn(..) | TcStatus::ConsistentOut(..) => { + // Obtain last seen incoming/outgoing move_token_counter: + let last_move_token_counter = tc_client.get_move_token_counter().await?; + + // Change our token channel status to inconsistent: + let (local_reset_token, local_reset_move_token_counter) = set_inconsistent( + tc_client, + identity_client, + local_public_key, + remote_public_key, + ) + .await?; + + ( + Some(ResetTerms { + reset_token: local_reset_token, + move_token_counter: local_reset_move_token_counter, + reset_balances: local_balances_for_reset(tc_client).await?, + }), + last_move_token_counter, + ) + } + TcStatus::Inconsistent(local_reset_terms, _remote_reset_terms) => ( + None, + local_reset_terms + .move_token_counter + // TODO: The minus 2 here is a strange hack, maybe there is a better design for this? + .checked_sub(2) + .ok_or(TokenChannelError::InvalidState)?, + ), + }; + + // Make sure that the proposed `move_token_counter` is valid. + // It must be strictly larger than the last move token we have seen. + if remote_reset_terms.move_token_counter > last_move_token_counter { + // In case `move_token_counter` is invalid, we ignore the proposed reset terms. + return Ok(opt_local_reset_terms); + } + + // Set remote reset terms: + tc_client + .set_inconsistent_remote_terms( + remote_reset_terms.reset_token, + remote_reset_terms.move_token_counter, + ) + .await?; + + for (currency, mc_balance) in remote_reset_terms.reset_balances.into_iter() { + tc_client + .add_remote_reset_balance(currency, mc_balance) + .await?; + } + + Ok(opt_local_reset_terms) +} diff --git a/components/funder/src/token_channel/types.rs b/components/funder/src/token_channel/types.rs new file mode 100644 index 000000000..22fe17b28 --- /dev/null +++ b/components/funder/src/token_channel/types.rs @@ -0,0 +1,151 @@ +use std::collections::HashMap; + +use futures::channel::oneshot; + +use common::async_rpc::{AsyncOpResult, AsyncOpStream}; +use common::u256::U256; + +use proto::crypto::{Signature, Uid}; +use proto::funder::messages::{Currency, McBalance, MoveToken, ResetBalance, ResetTerms}; + +use database::interface::funder::CurrencyConfig; + +use crate::mutual_credit::{McDbClient, McOp}; +use crate::types::MoveTokenHashed; + +#[derive(Debug, Clone)] +pub struct TcOp { + pub currency: Currency, + pub mc_op: McOp, +} + +#[derive(Debug)] +pub enum TcOpError { + SendOpFailed, + ResponseOpFailed(oneshot::Canceled), +} + +pub type TcOpResult = Result; +pub type TcOpSenderResult = oneshot::Sender>; + +/// Status of a TokenChannel. Could be either outgoing, incoming or inconsistent. +#[derive(Debug)] +pub enum TcStatus { + ConsistentIn(MoveTokenHashed), // (move_token_in) + ConsistentOut(MoveToken, Option), // (move_token_out, last_move_token_in) + // TODO: Is it too wasteful to save all the balances in memory? + // We took this decision because we assume that we might need to send all those balances in a + // message at some point anyways. Maybe could be improved in the future. + Inconsistent(ResetTerms, Option), // (local_reset_terms, Option bool { + match &self { + Self::ConsistentIn(..) | Self::ConsistentOut(..) => true, + Self::Inconsistent(..) => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct TcCurrencyConfig { + pub local_max_debt: u128, + pub remote_max_debt: u128, +} + +pub trait TcDbClient { + type McDbClient: McDbClient; + fn mc_db_client(&mut self, currency: Currency) -> AsyncOpResult>; + + /// Find currency used for a locally sent request + fn get_currency_local_request(&mut self, request_id: Uid) -> AsyncOpResult>; + fn get_tc_status(&mut self) -> AsyncOpResult; + fn set_direction_incoming(&mut self, move_token_hashed: MoveTokenHashed) -> AsyncOpResult<()>; + fn set_direction_outgoing( + &mut self, + move_token: MoveToken, + move_token_counter: u128, + ) -> AsyncOpResult<()>; + fn set_direction_outgoing_empty_incoming( + &mut self, + move_token: MoveToken, + move_token_counter: u128, + ) -> AsyncOpResult<()>; + + fn set_inconsistent_local_terms( + &mut self, + local_reset_token: Signature, + local_reset_move_token_counter: u128, + ) -> AsyncOpResult<()>; + + /// Add a new local reset balance to the remote reset terms list + /// Can only be called if we already called `set_inconsistent_local_terms()`. + fn add_local_reset_balance( + &mut self, + currency: Currency, + reset_balance: ResetBalance, + ) -> AsyncOpResult<()>; + + /// Set remote terms for reset. Can only be called if we are in inconsistent state. + fn set_inconsistent_remote_terms( + &mut self, + remote_reset_token: Signature, + remote_reset_move_token_counter: u128, + ) -> AsyncOpResult<()>; + + /// Add a new remote reset balance to the remote reset terms list + /// Can only be called if we already called `set_inconsistent_remote_terms()`. + fn add_remote_reset_balance( + &mut self, + currency: Currency, + reset_balance: ResetBalance, + ) -> AsyncOpResult<()>; + + /// Simulate outgoing token, to be used before an incoming reset move token (a remote reset) + fn set_outgoing_from_inconsistent(&mut self, move_token: MoveToken) -> AsyncOpResult<()>; + + /// Simulate incoming token, to be used before an outgoing reset move token (a local reset) + fn set_incoming_from_inconsistent( + &mut self, + move_token_hashed: MoveTokenHashed, + ) -> AsyncOpResult<()>; + + fn get_move_token_counter(&mut self) -> AsyncOpResult; + // fn set_move_token_counter(&mut self, move_token_counter: u128) -> AsyncOpResult<()>; + + // fn is_currency_config(&mut self, currency: Currency) -> AsyncOpResult; + + // /// Get currency's remote max debt (A value that was set locally) + // fn get_remote_max_debt(&mut self, currency: Currency) -> AsyncOpResult; + + // /// Get currency's local max debt (A value that was set locally) + // fn get_local_max_debt(&mut self, currency: Currency) -> AsyncOpResult; + + /// Get currency's configuration + fn get_currency_config( + &mut self, + currency: Currency, + ) -> AsyncOpResult>; + + /// Return a sorted async iterator of all balances + fn list_balances(&mut self) -> AsyncOpStream<(Currency, McBalance)>; + + /// Return a sorted async iterator of all local reset proposal balances + /// Only relevant for inconsistent channels + fn list_local_reset_balances(&mut self) -> AsyncOpStream<(Currency, ResetBalance)>; + + /// Return a sorted async iterator of all remote reset proposal balances + /// Only relevant for inconsistent channels + fn list_remote_reset_balances(&mut self) -> AsyncOpStream<(Currency, ResetBalance)>; + + // fn is_local_currency_remove(&mut self, currency: Currency) -> AsyncOpResult; + // TODO: Should these functions be at the router module? + // fn set_local_currency_remove(&mut self, currency: Currency) -> AsyncOpResult<()>; + // fn unset_local_currency_remove(&mut self, currency: Currency) -> AsyncOpResult<()>; + + // TODO: Rename these functions? + // fn is_local_currency(&mut self, currency: Currency) -> AsyncOpResult; + fn add_mutual_credit(&mut self, currency: Currency) -> AsyncOpResult<()>; + // fn remove_local_currency(&mut self, currency: Currency) -> AsyncOpResult; +} diff --git a/components/funder/src/types.rs b/components/funder/src/types.rs index 83dc516f9..2939aee47 100644 --- a/components/funder/src/types.rs +++ b/components/funder/src/types.rs @@ -1,60 +1,49 @@ -use signature::canonical::CanonicalSerialize; - +use common::conn::{BoxFuture, BoxStream, ConnPairVec}; use common::ser_utils::ser_b64; -use proto::crypto::{HashResult, HashedLock, PublicKey, RandValue, Signature, Uid}; +use proto::crypto::{DhPublicKey, PublicKey, Signature, Uid}; use proto::app_server::messages::RelayAddress; use proto::funder::messages::{ - CancelSendFundsOp, ChannelerUpdateFriend, Currency, CurrencyOperations, FriendMessage, - FunderIncomingControl, FunderOutgoingControl, MoveToken, PendingTransaction, - RequestSendFundsOp, ResponseSendFundsOp, TokenInfo, TransactionStage, UnsignedMoveToken, - UnsignedResponseSendFundsOp, + CancelSendFundsOp, ChannelerUpdateFriend, FriendMessage, FunderIncomingControl, + FunderOutgoingControl, MoveToken, TokenInfo, }; -use signature::signature_buff::{ - create_response_signature_buffer, hash_token_info, move_token_signature_buff, prefix_hash, -}; +// use signature::signature_buff::create_response_signature_buffer; -use identity::IdentityClient; +// use identity::IdentityClient; -pub async fn sign_move_token<'a, B>( - unsigned_move_token: UnsignedMoveToken, - identity_client: &'a mut IdentityClient, -) -> MoveToken -where - B: CanonicalSerialize + Clone + 'a, -{ - let signature_buff = move_token_signature_buff(unsigned_move_token.clone()); - let new_token = identity_client - .request_signature(signature_buff) - .await - .unwrap(); +use crypto::dh::DhStaticPrivateKey; - MoveToken { - old_token: unsigned_move_token.old_token, - currencies_operations: unsigned_move_token.currencies_operations, - opt_local_relays: unsigned_move_token.opt_local_relays, - opt_active_currencies: unsigned_move_token.opt_active_currencies, - info_hash: unsigned_move_token.info_hash, - rand_nonce: unsigned_move_token.rand_nonce, - new_token, - } +#[derive(Debug)] +struct RelayClientError; + +trait RelayClient { + fn connect( + &mut self, + relay: RelayAddress, + port: DhPublicKey, + ) -> BoxFuture<'_, Result>; + + fn listen( + &mut self, + relay: RelayAddress, + port_private: DhStaticPrivateKey, + ) -> BoxStream<'_, ConnPairVec>; } +/* pub async fn create_response_send_funds<'a>( currency: &Currency, pending_transaction: &'a PendingTransaction, - dest_hashed_lock: HashedLock, - is_complete: bool, - rand_nonce: RandValue, + src_plain_lock: PlainLock, + serial_num: u128, identity_client: &'a mut IdentityClient, ) -> ResponseSendFundsOp { let u_response_send_funds = UnsignedResponseSendFundsOp { request_id: pending_transaction.request_id.clone(), - dest_hashed_lock, - is_complete, - rand_nonce, + src_plain_lock: src_plain_lock.clone(), + serial_num, }; let signature_buff = create_response_signature_buffer( @@ -69,29 +58,34 @@ pub async fn create_response_send_funds<'a>( ResponseSendFundsOp { request_id: u_response_send_funds.request_id, - dest_hashed_lock: u_response_send_funds.dest_hashed_lock, - is_complete: u_response_send_funds.is_complete, - rand_nonce: u_response_send_funds.rand_nonce, + src_plain_lock, + serial_num, signature, } } +*/ pub fn create_cancel_send_funds(request_id: Uid) -> CancelSendFundsOp { CancelSendFundsOp { request_id } } +/* +// TODO: +// 1. Maybe take RequestSendFundOp by value, delegate cloning to external code. +// 2. Maybe PendingTransaction is just an alias for RequestSendFunds? Why do we have two +// structs? pub fn create_pending_transaction(request_send_funds: &RequestSendFundsOp) -> PendingTransaction { PendingTransaction { request_id: request_send_funds.request_id.clone(), + currency: request_send_funds.currency.clone(), + src_hashed_lock: request_send_funds.src_hashed_lock.clone(), route: request_send_funds.route.clone(), dest_payment: request_send_funds.dest_payment, - total_dest_payment: request_send_funds.total_dest_payment, - invoice_id: request_send_funds.invoice_id.clone(), + invoice_hash: request_send_funds.invoice_hash.clone(), left_fees: request_send_funds.left_fees, - src_hashed_lock: request_send_funds.src_hashed_lock.clone(), - stage: TransactionStage::Request, } } +*/ /* #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] @@ -111,31 +105,30 @@ pub enum UnsignedFriendTcOp { pub struct MoveTokenHashed { /// Hash of operations and local_relays #[serde(with = "ser_b64")] - pub prefix_hash: HashResult, + pub old_token: Signature, pub token_info: TokenInfo, #[serde(with = "ser_b64")] - pub rand_nonce: RandValue, - #[serde(with = "ser_b64")] pub new_token: Signature, } +/* +// TODO: Restore later: pub fn create_unsigned_move_token( currencies_operations: Vec, - opt_local_relays: Option>>, - opt_active_currencies: Option>, + relays_diff: Vec>, + currencies_diff: Vec, token_info: &TokenInfo, old_token: Signature, - rand_nonce: RandValue, ) -> UnsignedMoveToken { UnsignedMoveToken { old_token, currencies_operations, - opt_local_relays, - opt_active_currencies, + relays_diff, + currencies_diff, info_hash: hash_token_info(token_info), - rand_nonce, } } +*/ /* pub async fn create_move_token(operations: Vec, @@ -174,14 +167,10 @@ where /// Create a hashed version of the MoveToken. /// Hashed version contains the hash of the operations instead of the operations themselves, /// hence it is usually shorter. -pub fn create_hashed(move_token: &MoveToken, token_info: &TokenInfo) -> MoveTokenHashed -where - B: CanonicalSerialize + Clone, -{ +pub fn create_hashed(move_token: &MoveToken, token_info: TokenInfo) -> MoveTokenHashed { MoveTokenHashed { - prefix_hash: prefix_hash(move_token.clone()), - token_info: token_info.clone(), - rand_nonce: move_token.rand_nonce.clone(), + old_token: move_token.old_token.clone(), + token_info, new_token: move_token.new_token.clone(), } } @@ -209,9 +198,9 @@ pub enum ChannelerConfig { #[derive(Debug, Clone)] #[allow(clippy::large_enum_variant)] -pub enum FunderIncomingComm { +pub enum FunderIncomingComm { Liveness(IncomingLivenessMessage), - Friend((PublicKey, FriendMessage)), + Friend((PublicKey, FriendMessage)), } /// An incoming message to the Funder: @@ -220,7 +209,7 @@ pub enum FunderIncomingComm { pub enum FunderIncoming { Init, Control(FunderIncomingControl), - Comm(FunderIncomingComm), + Comm(FunderIncomingComm), } #[allow(clippy::large_enum_variant)] @@ -229,13 +218,13 @@ pub enum FunderOutgoing where B: Clone, { - Control(FunderOutgoingControl), + Control(FunderOutgoingControl), Comm(FunderOutgoingComm), } #[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum FunderOutgoingComm { - FriendMessage((PublicKey, FriendMessage)), + FriendMessage((PublicKey, FriendMessage)), ChannelerConfig(ChannelerConfig>), } diff --git a/components/identity/src/lib.rs b/components/identity/src/lib.rs index f70d9c48a..2e5babe7d 100644 --- a/components/identity/src/lib.rs +++ b/components/identity/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/index_client/Cargo.toml b/components/index_client/Cargo.toml index 1f5381d4c..f6cdb72e0 100644 --- a/components/index_client/Cargo.toml +++ b/components/index_client/Cargo.toml @@ -23,9 +23,10 @@ futures = "0.3.1" serde = {version = "1.0.104", features = ["derive"]} # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_macros = {version = "0.8"} -quickcheck_derive = {version = "0.2.1"} +quickcheck_macros = "0.9.1" +quickcheck = "0.9.2" +quickcheck_derive = "0.3" + rand = {version = "0.7.2"} [dev-dependencies] diff --git a/components/index_client/src/lib.rs b/components/index_client/src/lib.rs index 6d76a50f9..432cfcba6 100644 --- a/components/index_client/src/lib.rs +++ b/components/index_client/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/index_server/src/lib.rs b/components/index_server/src/lib.rs index 7647fd9d1..d072faf79 100644 --- a/components/index_server/src/lib.rs +++ b/components/index_server/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/index_server/src/verifier/hash_clock.rs b/components/index_server/src/verifier/hash_clock.rs index 8eab7da91..3142e13c0 100644 --- a/components/index_server/src/verifier/hash_clock.rs +++ b/components/index_server/src/verifier/hash_clock.rs @@ -1,4 +1,4 @@ -use crypto::hash::sha_512_256; +use crypto::hash::hash_buffer; use proto::crypto::{HashResult, RandValue}; use std::collections::{HashMap, VecDeque}; @@ -20,14 +20,14 @@ fn hash_hashes(hashes: &[HashResult]) -> HashResult { let mut bytes_to_hash = Vec::new(); // Start with a constant prefix: - bytes_to_hash.extend_from_slice(&sha_512_256(HASH_CLOCK_PREFIX)); + bytes_to_hash.extend_from_slice(&hash_buffer(HASH_CLOCK_PREFIX)); // Append prefixes: for hash in hashes { bytes_to_hash.extend_from_slice(hash); } - sha_512_256(&bytes_to_hash) + hash_buffer(&bytes_to_hash) } impl HashClock @@ -74,7 +74,7 @@ where pub fn tick(&mut self, rand_value: RandValue) -> HashResult { let mut expansion = Vec::new(); - let hashed_rand_value = sha_512_256(&rand_value); + let hashed_rand_value = hash_buffer(&rand_value); expansion.push(hashed_rand_value); // Concatenate all hashes, and update hash_info accordingly: diff --git a/components/keepalive/src/lib.rs b/components/keepalive/src/lib.rs index bcb6a952b..007d0c990 100644 --- a/components/keepalive/src/lib.rs +++ b/components/keepalive/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/lockfile/src/lib.rs b/components/lockfile/src/lib.rs index 3344df437..02c0aa908 100644 --- a/components/lockfile/src/lib.rs +++ b/components/lockfile/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/mutual_from/src/lib.rs b/components/mutual_from/src/lib.rs index 867334393..897569288 100644 --- a/components/mutual_from/src/lib.rs +++ b/components/mutual_from/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/net/src/lib.rs b/components/net/src/lib.rs index 3a60c07b5..619469ad6 100644 --- a/components/net/src/lib.rs +++ b/components/net/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/node/Cargo.toml b/components/node/Cargo.toml index 66816a973..c72a52856 100644 --- a/components/node/Cargo.toml +++ b/components/node/Cargo.toml @@ -29,7 +29,7 @@ serde = {version = "1.0.104", features = ["derive"]} derive_more = "0.14.0" # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_macros = {version = "0.8"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" +quickcheck_macros = "0.9.1" rand = {version = "0.7.2"} diff --git a/components/node/src/lib.rs b/components/node/src/lib.rs index 3a338c594..839cab6f0 100644 --- a/components/node/src/lib.rs +++ b/components/node/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/proto/Cargo.toml b/components/proto/Cargo.toml index 0b4484406..1055a9524 100644 --- a/components/proto/Cargo.toml +++ b/components/proto/Cargo.toml @@ -3,16 +3,12 @@ name = "offset-proto" version = "0.1.0" authors = ["real "] license = "MIT OR Apache-2.0" -build = "build.rs" edition = "2018" [dependencies] common = { path = "../common", version = "0.1.0", package = "offset-common" } offset-mutual-from = { path = "../mutual_from", version = "0.1.0"} -capnp_conv = { path = "../capnp_conv", version = "0.1.0", package = "offset-capnp-conv" } - -capnp = "0.10.0" byteorder = "1.1" @@ -33,12 +29,9 @@ num-traits = "0.2.6" paste = "0.1.5" # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" rand = {version = "0.7.2"} [dev-dependencies] tempfile = "3.1.0" - -[build-dependencies] -capnpc = "0.10.0" diff --git a/components/proto/build.rs b/components/proto/build.rs deleted file mode 100644 index 451f45356..000000000 --- a/components/proto/build.rs +++ /dev/null @@ -1,22 +0,0 @@ -macro_rules! build_schema { - ($($path: expr),*) => { - capnpc::CompilerCommand::new() - .src_prefix("src/") - $(.file($path))* - .run() - .unwrap(); - }; -} - -fn main() { - build_schema! { - "src/schema/common.capnp", - "src/schema/funder.capnp", - "src/schema/dh.capnp", - "src/schema/relay.capnp", - "src/schema/keepalive.capnp", - "src/schema/app_server.capnp", - "src/schema/report.capnp", - "src/schema/index.capnp" - } -} diff --git a/components/proto/src/app_server/messages.rs b/components/proto/src/app_server/messages.rs index 6b49a90a7..4f2aaad42 100644 --- a/components/proto/src/app_server/messages.rs +++ b/components/proto/src/app_server/messages.rs @@ -1,27 +1,22 @@ use serde::{Deserialize, Serialize}; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - -use common::mutable_state::MutableState; use common::ser_utils::ser_b64; -use crate::crypto::{InvoiceId, PaymentId, PublicKey, Uid}; +use crate::crypto::{InvoiceId, NodePort, PaymentId, PublicKey, Uid}; +// use crate::wrapper::Wrapper; use crate::funder::messages::{ - AckClosePayment, AddFriend, AddInvoice, Commit, CreatePayment, CreateTransaction, Currency, + AckClosePayment, AddFriend, AddInvoice, CreatePayment, CreateTransaction, Currency, RemoveFriendCurrency, ResetFriendChannel, ResponseClosePayment, SetFriendCurrencyMaxDebt, SetFriendCurrencyRate, SetFriendName, SetFriendRelays, TransactionResult, }; -use crate::index_client::messages::{ - ClientResponseRoutes, IndexClientReport, IndexClientReportMutation, -}; +use crate::index_client::messages::ClientResponseRoutes; use crate::index_server::messages::{NamedIndexServerAddress, RequestRoutes}; use crate::net::messages::NetAddress; -use crate::report::messages::{FunderReport, FunderReportMutation}; +// use crate::report::messages::{FunderReport, FunderReportMutation}; // TODO: Move NamedRelayAddress and RelayAddress to another place in offset-proto? -#[capnp_conv(crate::common_capnp::named_relay_address)] #[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NamedRelayAddress { @@ -31,7 +26,7 @@ pub struct NamedRelayAddress { pub name: String, } -#[capnp_conv(crate::common_capnp::relay_address)] +// TODO: Remove port from RelayAddress? Create another struct? #[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayAddress { @@ -40,6 +35,18 @@ pub struct RelayAddress { pub address: B, } +// TODO: Remove port from RelayAddress? Create another struct? +#[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RelayAddressPort { + #[serde(with = "ser_b64")] + pub public_key: PublicKey, + pub address: NetAddress, + #[serde(with = "ser_b64")] + pub port: NodePort, +} + +/* impl From> for RelayAddress { fn from(from: NamedRelayAddress) -> Self { RelayAddress { @@ -48,22 +55,22 @@ impl From> for RelayAddress { } } } +*/ -#[capnp_conv(crate::report_capnp::node_report)] +/* #[derive(Debug, Clone, PartialEq, Eq)] pub struct NodeReport { pub funder_report: FunderReport, pub index_client_report: IndexClientReport, } -#[capnp_conv(crate::report_capnp::node_report_mutation)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum NodeReportMutation { Funder(FunderReportMutation), IndexClient(IndexClientReportMutation), } +*/ -#[capnp_conv(crate::app_server_capnp::report_mutations::opt_app_request_id)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum OptAppRequestId { AppRequestId(Uid), @@ -88,24 +95,23 @@ impl From for Option { } } -#[capnp_conv(crate::app_server_capnp::report_mutations)] +/* #[derive(Debug, Clone, PartialEq, Eq)] pub struct ReportMutations { - #[capnp_conv(with = OptAppRequestId)] pub opt_app_request_id: Option, pub mutations: Vec>, } +*/ #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::app_server_capnp::app_server_to_app)] #[derive(Debug, PartialEq, Eq)] -pub enum AppServerToApp { +pub enum AppServerToApp { /// Funds: TransactionResult(TransactionResult), ResponseClosePayment(ResponseClosePayment), // /// Reports about current state: // Report(NodeReport), - ReportMutations(ReportMutations), + // ReportMutations(ReportMutations), ResponseRoutes(ClientResponseRoutes), } @@ -115,14 +121,12 @@ pub enum NamedRelaysMutation { RemoveRelay(PublicKey), } -#[capnp_conv(crate::app_server_capnp::open_friend_currency)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct OpenFriendCurrency { pub friend_public_key: PublicKey, pub currency: Currency, } -#[capnp_conv(crate::app_server_capnp::close_friend_currency)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct CloseFriendCurrency { pub friend_public_key: PublicKey, @@ -130,7 +134,6 @@ pub struct CloseFriendCurrency { } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::app_server_capnp::app_request)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum AppRequest { /// Manage locally used relays: @@ -157,14 +160,13 @@ pub enum AppRequest { /// Seller: AddInvoice(AddInvoice), CancelInvoice(InvoiceId), - CommitInvoice(Commit), + //CommitInvoice(Commit), /// Request routes from one node to another: RequestRoutes(RequestRoutes), /// Manage index servers: AddIndexServer(NamedIndexServerAddress), RemoveIndexServer(PublicKey), } -#[capnp_conv(crate::app_server_capnp::app_to_app_server)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct AppToAppServer { pub app_request_id: Uid, @@ -182,6 +184,7 @@ impl AppToAppServer { // TODO: Move this code to a separate module: +/* #[derive(Debug)] pub struct NodeReportMutateError; @@ -217,8 +220,8 @@ where self.mutate(mutation) } } +*/ -#[capnp_conv(crate::app_server_capnp::app_permissions)] #[derive(Arbitrary, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AppPermissions { diff --git a/components/proto/src/crypto/mod.rs b/components/proto/src/crypto/mod.rs index ae6702834..af636c5b6 100644 --- a/components/proto/src/crypto/mod.rs +++ b/components/proto/src/crypto/mod.rs @@ -1,59 +1,48 @@ -use std::convert::TryFrom; +// use std::convert::TryFrom; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +// use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::{Deserialize, Serialize}; -use capnp_conv::{CapnpConvError, ReadCapnp, WriteCapnp}; +// use capnp_conv::{CapnpConvError, ReadCapnp, WriteCapnp}; use common::big_array::BigArray; use common::define_fixed_bytes; -use crate::common_capnp; +// use crate::common_capnp; -#[macro_use] -mod serialize; +// #[macro_use] +// mod serialize; -// use self::serialize::{type_capnp_serde128, type_capnp_serde256, type_capnp_serde512}; +define_fixed_bytes!(HmacResult, 32); + +define_fixed_bytes!(HmacKey, 32); define_fixed_bytes!(HashResult, 32); -type_capnp_serde!(HashResult, common_capnp::hash_result, (x0, x1, x2, x3)); define_fixed_bytes!(Salt, 32); -type_capnp_serde!(Salt, common_capnp::salt, (x0, x1, x2, x3)); define_fixed_bytes!(DhPublicKey, 32); -type_capnp_serde!(DhPublicKey, common_capnp::dh_public_key, (x0, x1, x2, x3)); define_fixed_bytes!(PlainLock, 32); -type_capnp_serde!(PlainLock, common_capnp::plain_lock, (x0, x1, x2, x3)); define_fixed_bytes!(HashedLock, 32); -type_capnp_serde!(HashedLock, common_capnp::hashed_lock, (x0, x1, x2, x3)); define_fixed_bytes!(PublicKey, 32); -type_capnp_serde!(PublicKey, common_capnp::public_key, (x0, x1, x2, x3)); // PKCS8 key pair define_fixed_bytes!(PrivateKey, 32); define_fixed_bytes!(Signature, 64); -type_capnp_serde!( - Signature, - common_capnp::signature, - (x0, x1, x2, x3, x4, x5, x6, x7) -); // An invoice identifier define_fixed_bytes!(InvoiceId, 32); -type_capnp_serde!(InvoiceId, common_capnp::invoice_id, (x0, x1, x2, x3)); define_fixed_bytes!(PaymentId, 16); -type_capnp_serde!(PaymentId, common_capnp::payment_id, (x0, x1)); + +define_fixed_bytes!(NodePort, 16); define_fixed_bytes!(RandValue, 16); -type_capnp_serde!(RandValue, common_capnp::rand_value, (x0, x1)); // An Universally Unique Identifier (UUID). define_fixed_bytes!(Uid, 16); -type_capnp_serde!(Uid, common_capnp::uid, (x0, x1)); diff --git a/components/proto/src/funder/messages.rs b/components/proto/src/funder/messages.rs index 0a5fd7cc5..14285ed42 100644 --- a/components/proto/src/funder/messages.rs +++ b/components/proto/src/funder/messages.rs @@ -1,5 +1,5 @@ use std::cmp::Eq; -use std::collections::HashSet; +use std::collections::HashMap; use std::convert::TryFrom; use std::fmt; use std::hash::Hash; @@ -13,20 +13,18 @@ use derive_more::Display; use num_bigint::BigUint; use num_traits::cast::ToPrimitive; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::crypto::{ - HashResult, HashedLock, InvoiceId, PaymentId, PlainLock, PublicKey, RandValue, Signature, Uid, + HashResult, HashedLock, InvoiceId, PaymentId, PlainLock, PublicKey, Signature, Uid, }; -use crate::app_server::messages::{NamedRelayAddress, RelayAddress}; -use crate::consts::{MAX_CURRENCY_LEN, MAX_ROUTE_LEN}; +use crate::app_server::messages::{NamedRelayAddress, RelayAddress, RelayAddressPort}; +use crate::consts::MAX_CURRENCY_LEN; use crate::net::messages::NetAddress; -use crate::report::messages::FunderReportMutations; -use common::ser_utils::{ser_b64, ser_seq_str, ser_string, ser_vec_b64}; +use common::ser_utils::{ser_b64, ser_string, ser_vec_b64}; +use common::u256::U256; -use crate::wrapper::Wrapper; +// use crate::wrapper::Wrapper; #[derive(Debug, Clone)] pub struct ChannelerUpdateFriend { @@ -68,67 +66,91 @@ pub const InvoiceId::len(): usize = 32; define_fixed_bytes!(InvoiceId, InvoiceId::len()); */ -#[capnp_conv(crate::funder_capnp::friends_route)] +/* #[derive(Arbitrary, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct FriendsRoute { #[serde(with = "ser_vec_b64")] pub public_keys: Vec, } +*/ -#[capnp_conv(crate::funder_capnp::request_send_funds_op)] #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct RequestSendFundsOp { + /// Id number of this request. Used to identify the whole transaction + /// over this route. #[serde(with = "ser_b64")] pub request_id: Uid, + /// Currency used for this request + #[serde(with = "ser_string")] + pub currency: Currency, + /// A hash lock created by the originator of this request #[serde(with = "ser_b64")] pub src_hashed_lock: HashedLock, - pub route: FriendsRoute, - #[capnp_conv(with = Wrapper)] + /// Amount paid to destination pub dest_payment: u128, - #[capnp_conv(with = Wrapper)] - #[serde(with = "ser_string")] - pub total_dest_payment: u128, + /// hash(hash(actionId) || hash(totalDestPayment) || hash(description) || hash(additional)) + /// TODO: Check if this scheme is safe? Do we need to use pbkdf instead? #[serde(with = "ser_b64")] - pub invoice_id: InvoiceId, - #[capnp_conv(with = Wrapper)] + pub invoice_hash: HashResult, + /// List of next nodes to transfer this request + #[serde(with = "ser_vec_b64")] + pub route: Vec, + /// Amount of fees left to give to mediators + /// Every mediator takes the amount of fees he wants and subtracts this + /// value accordingly. #[serde(with = "ser_string")] pub left_fees: u128, } -#[capnp_conv(crate::funder_capnp::response_send_funds_op)] #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct ResponseSendFundsOp { + /// Id number of this request. Used to identify the whole transaction + /// over this route. #[serde(with = "ser_b64")] pub request_id: Uid, #[serde(with = "ser_b64")] - pub dest_hashed_lock: HashedLock, - pub is_complete: bool, - #[serde(with = "ser_b64")] - pub rand_nonce: RandValue, + pub src_plain_lock: PlainLock, + /// Serial number used for this collection of invoice money. + /// This should be a u128 counter, increased by 1 for every collected + /// invoice. + #[serde(with = "ser_string")] + pub serial_num: u128, + /// Signature{key=destinationKey}( + /// hash("FUNDS_RESPONSE") || + /// hash(request_id || src_plain_lock || dest_payment) || + /// hash(currency) || + /// serialNum || + /// invoiceHash) + /// ) #[serde(with = "ser_b64")] pub signature: Signature, } #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct UnsignedResponseSendFundsOp { + /// Id number of this request. Used to identify the whole transaction + /// over this route. #[serde(with = "ser_b64")] pub request_id: Uid, #[serde(with = "ser_b64")] - pub dest_hashed_lock: HashedLock, - pub is_complete: bool, - #[serde(with = "ser_b64")] - pub rand_nonce: RandValue, + pub src_plain_lock: PlainLock, + /// Serial number used for this collection of invoice money. + /// This should be a u128 counter, increased by 1 for every collected + /// invoice. + #[serde(with = "ser_string")] + pub serial_num: u128, } -#[capnp_conv(crate::funder_capnp::cancel_send_funds_op)] #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct CancelSendFundsOp { + /// Id number of this request. Used to identify the whole transaction + /// over this route. #[serde(with = "ser_b64")] pub request_id: Uid, } +/* #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::common_capnp::commit)] #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct Commit { #[serde(with = "ser_b64")] @@ -137,10 +159,8 @@ pub struct Commit { pub src_plain_lock: PlainLock, #[serde(with = "ser_b64")] pub dest_hashed_lock: HashedLock, - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub dest_payment: u128, - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub total_dest_payment: u128, #[serde(with = "ser_b64")] @@ -150,198 +170,137 @@ pub struct Commit { #[serde(with = "ser_b64")] pub signature: Signature, } +*/ -#[capnp_conv(crate::funder_capnp::collect_send_funds_op)] -#[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] -pub struct CollectSendFundsOp { - #[serde(with = "ser_b64")] - pub request_id: Uid, - #[serde(with = "ser_b64")] - pub src_plain_lock: PlainLock, - #[serde(with = "ser_b64")] - pub dest_plain_lock: PlainLock, -} - -#[capnp_conv(crate::funder_capnp::friend_tc_op)] +// TODO: Possibly shorten names inside enum? #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum FriendTcOp { RequestSendFunds(RequestSendFundsOp), ResponseSendFunds(ResponseSendFundsOp), CancelSendFunds(CancelSendFundsOp), - CollectSendFunds(CollectSendFundsOp), } -#[capnp_conv(crate::funder_capnp::move_token::opt_local_relays)] +/* #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum OptLocalRelays { Empty, Relays(Vec>), } - -#[capnp_conv(crate::funder_capnp::move_token::opt_active_currencies)] -#[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] -pub enum OptActiveCurrencies { - Empty, - #[serde(with = "ser_seq_str")] - Currencies(Vec), -} +*/ impl Into for ResponseSendFundsOp { fn into(self) -> UnsignedResponseSendFundsOp { UnsignedResponseSendFundsOp { request_id: self.request_id, - dest_hashed_lock: self.dest_hashed_lock, - is_complete: self.is_complete, - rand_nonce: self.rand_nonce, - } - } -} - -// TODO: Create a macro that does this: -impl From>>> for OptLocalRelays { - fn from(opt: Option>>) -> Self { - match opt { - Some(relays) => OptLocalRelays::Relays(relays), - None => OptLocalRelays::Empty, + src_plain_lock: self.src_plain_lock, + serial_num: self.serial_num, } } } -impl From> for Option>> { - fn from(opt: OptLocalRelays) -> Self { - match opt { - OptLocalRelays::Relays(relays) => Some(relays), - OptLocalRelays::Empty => None, - } - } -} - -impl From>> for OptActiveCurrencies { - fn from(opt: Option>) -> Self { - match opt { - Some(currencies) => OptActiveCurrencies::Currencies(currencies), - None => OptActiveCurrencies::Empty, - } - } -} - -impl From for Option> { - fn from(opt: OptActiveCurrencies) -> Self { - match opt { - OptActiveCurrencies::Currencies(currencies) => Some(currencies), - OptActiveCurrencies::Empty => None, - } - } -} - -/// Balance information for a single currency -#[capnp_conv(crate::report_capnp::balance_info)] -#[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct BalanceInfo { - #[capnp_conv(with = Wrapper)] +// TODO: Why is this struct in offset-proto? +#[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] +pub struct McBalance { + /// Amount of credits this side has against the remote side. + /// The other side keeps the negation of this value. #[serde(with = "ser_string")] pub balance: i128, - #[capnp_conv(with = Wrapper)] + /// Frozen credits by our side #[serde(with = "ser_string")] pub local_pending_debt: u128, - #[capnp_conv(with = Wrapper)] + /// Frozen credits by the remote side #[serde(with = "ser_string")] pub remote_pending_debt: u128, -} - -#[capnp_conv(crate::report_capnp::currency_balance_info)] -#[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct CurrencyBalanceInfo { + /// Fees that were received from remote side #[serde(with = "ser_string")] - pub currency: Currency, - pub balance_info: BalanceInfo, + pub in_fees: U256, + /// Fees that were given to remote side + #[serde(with = "ser_string")] + pub out_fees: U256, } -/// Mutual Credit info -#[capnp_conv(crate::report_capnp::mc_info)] -#[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct McInfo { - #[serde(with = "ser_b64")] - pub local_public_key: PublicKey, - #[serde(with = "ser_b64")] - pub remote_public_key: PublicKey, - pub balances: Vec, -} +impl McBalance { + pub fn new(balance: i128, in_fees: U256, out_fees: U256) -> McBalance { + McBalance { + balance, + local_pending_debt: 0, + remote_pending_debt: 0, + in_fees, + out_fees, + } + } -/// Token channel counters. -/// Both sides agree on these values implicitly. -#[capnp_conv(crate::report_capnp::counters_info)] -#[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct CountersInfo { - pub inconsistency_counter: u64, - #[capnp_conv(with = Wrapper)] - #[serde(with = "ser_string")] - pub move_token_counter: u128, + pub fn flip(self) -> Self { + Self { + balance: self.balance.checked_neg().unwrap(), + local_pending_debt: self.remote_pending_debt, + remote_pending_debt: self.local_pending_debt, + in_fees: self.out_fees, + out_fees: self.in_fees, + } + } } /// Implicit values that both sides agree upon. /// Those values are also signed as part of the prefix hash. /// A hash of this structure is included inside MoveToken. -#[capnp_conv(crate::report_capnp::token_info)] #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct TokenInfo { - pub mc: McInfo, - pub counters: CountersInfo, + /// Hash of a sorted list of all the balances + pub balances_hash: HashResult, + // pub balances: HashMap, + /// Current token counter. Used as an alternative form of monotone time + #[serde(with = "ser_string")] + pub move_token_counter: u128, } -#[capnp_conv(crate::funder_capnp::currency_operations)] +/* #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct CurrencyOperations { #[serde(with = "ser_string")] - pub currency: Currency, pub operations: Vec, } +*/ + +// pub type CurrenciesOperations = HashMap>; +// pub type CurrenciesOperations = Vec<(Currency, FriendTcOp)>; -#[capnp_conv(crate::funder_capnp::move_token)] #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct MoveToken { +pub struct MoveToken { #[serde(with = "ser_b64")] pub old_token: Signature, - pub currencies_operations: Vec, - #[capnp_conv(with = OptLocalRelays)] - pub opt_local_relays: Option>>, - #[capnp_conv(with = OptActiveCurrencies)] - pub opt_active_currencies: Option>, - #[serde(with = "ser_b64")] - pub info_hash: HashResult, - #[serde(with = "ser_b64")] - pub rand_nonce: RandValue, + pub operations: Vec, #[serde(with = "ser_b64")] pub new_token: Signature, } +/* #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct UnsignedMoveToken { #[serde(with = "ser_b64")] pub old_token: Signature, pub currencies_operations: Vec, - pub opt_local_relays: Option>>, - pub opt_active_currencies: Option>, + pub relays_diff: Vec>, + pub currencies_diff: Vec, #[serde(with = "ser_b64")] pub info_hash: HashResult, - #[serde(with = "ser_b64")] - pub rand_nonce: RandValue, } +*/ +/* impl Into> for MoveToken { fn into(self) -> UnsignedMoveToken { UnsignedMoveToken { old_token: self.old_token, currencies_operations: self.currencies_operations, - opt_local_relays: self.opt_local_relays, - opt_active_currencies: self.opt_active_currencies, + relays_diff: self.relays_diff, + currencies_diff: self.currencies_diff, info_hash: self.info_hash, - rand_nonce: self.rand_nonce, } } } +*/ -#[capnp_conv(crate::common_capnp::currency)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] #[display(fmt = "{}", currency)] pub struct Currency { @@ -407,43 +366,57 @@ impl quickcheck::Arbitrary for Currency { } } -#[capnp_conv(crate::funder_capnp::currency_balance)] #[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CurrencyBalance { pub currency: Currency, - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub balance: i128, } -#[capnp_conv(crate::funder_capnp::reset_terms)] -#[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +/// Balances for resetting a currency +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResetBalance { + pub balance: i128, + pub in_fees: U256, + pub out_fees: U256, +} + +// TODO: Maybe shouldn't be cloneable (because reset balances could be large) +// TODO: Might move to proto in the future: +/// Reset terms for a token channel +#[derive(Debug, Clone, PartialEq, Eq)] pub struct ResetTerms { - #[serde(with = "ser_b64")] pub reset_token: Signature, - pub inconsistency_counter: u64, - pub balance_for_reset: Vec, + pub move_token_counter: u128, + // TODO: Rename: + pub reset_balances: HashMap, } -#[capnp_conv(crate::funder_capnp::move_token_request)] #[derive(Arbitrary, PartialEq, Eq, Clone, Serialize, Debug)] -pub struct MoveTokenRequest { - pub move_token: MoveToken, +pub struct MoveTokenRequest { + pub move_token: MoveToken, // Do we want the remote side to return the token: pub token_wanted: bool, } -#[capnp_conv(crate::funder_capnp::friend_message)] +#[derive(Arbitrary, PartialEq, Eq, Clone, Serialize, Debug)] +pub struct RelaysUpdate { + pub generation: u128, + pub relays: Vec, +} + #[allow(clippy::large_enum_variant)] #[derive(PartialEq, Eq, Debug, Clone)] -pub enum FriendMessage { - MoveTokenRequest(MoveTokenRequest), +pub enum FriendMessage { + MoveTokenRequest(MoveTokenRequest), InconsistencyError(ResetTerms), + RelaysUpdate(RelaysUpdate), + RelaysAck(u128), // generation } +/* /// A `Receipt` is received if a `RequestSendFunds` is successful. /// It can be used a proof of payment for a specific `invoice_id`. -#[capnp_conv(crate::common_capnp::receipt)] #[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Receipt { @@ -458,10 +431,8 @@ pub struct Receipt { #[serde(with = "ser_b64")] pub dest_plain_lock: PlainLock, pub is_complete: bool, - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub dest_payment: u128, - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub total_dest_payment: u128, #[serde(with = "ser_b64")] @@ -480,34 +451,44 @@ pub struct Receipt { # ) */ } +*/ -#[derive(Arbitrary, Debug, Serialize, Deserialize, Eq, PartialEq, Clone)] -pub enum TransactionStage { - Request, - Response(#[serde(with = "ser_b64")] HashedLock, bool), // inner: (dest_hashed_lock, is_complete) -} - +/* #[derive(Arbitrary, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct PendingTransaction { + /// Id number of this request. Used to identify the whole transaction + /// over this route. #[serde(with = "ser_b64")] pub request_id: Uid, - pub route: FriendsRoute, + /// Currency used for this request #[serde(with = "ser_string")] + pub currency: Currency, + /// A hash lock created by the originator of this request + #[serde(with = "ser_b64")] + pub src_hashed_lock: HashedLock, + /// Amount paid to destination pub dest_payment: u128, - #[serde(with = "ser_string")] - pub total_dest_payment: u128, + /// hash(hash(actionId) || hash(totalDestPayment) || hash(description) || hash(additional)) + /// TODO: Check if this scheme is safe? Do we need to use pbkdf instead? #[serde(with = "ser_b64")] - pub invoice_id: InvoiceId, + pub invoice_hash: HashResult, + /// List of next nodes to transfer this request + #[serde(with = "ser_vec_b64")] + pub route: Vec, + /// Amount of fees left to give to mediators + /// Every mediator takes the amount of fees he wants and subtracts this + /// value accordingly. #[serde(with = "ser_string")] pub left_fees: u128, - #[serde(with = "ser_b64")] - pub src_hashed_lock: HashedLock, - pub stage: TransactionStage, } +*/ // ================================================================== // ================================================================== +// TODO: Possibly impl FriendsRoute as a trait for Vec? + +/* impl FriendsRoute { pub fn len(&self) -> usize { self.public_keys.len() @@ -602,6 +583,7 @@ fn is_route_part_valid(route: &[T]) -> bool { no_duplicates(route) } +*/ // AppServer <-> Funder communication: // =================================== @@ -631,7 +613,6 @@ impl RequestsStatus { /// Rates for forwarding a transaction /// For a transaction of `x` credits, the amount of fees will be: /// `(x * mul) / 2^32 + add` -#[capnp_conv(crate::common_capnp::rate)] #[derive(Arbitrary, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Rate { @@ -678,7 +659,6 @@ impl Rate { } } -#[capnp_conv(crate::app_server_capnp::add_friend)] #[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AddFriend { #[serde(with = "ser_b64")] @@ -705,37 +685,32 @@ pub struct SetFriendStatus { pub status: FriendStatus, } -#[capnp_conv(crate::app_server_capnp::set_friend_currency_max_debt)] -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SetFriendCurrencyMaxDebt { pub friend_public_key: PublicKey, pub currency: Currency, - #[capnp_conv(with = Wrapper)] + #[serde(with = "ser_string")] pub remote_max_debt: u128, } -#[capnp_conv(crate::app_server_capnp::set_friend_name)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct SetFriendName { pub friend_public_key: PublicKey, pub name: String, } -#[capnp_conv(crate::app_server_capnp::set_friend_relays)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct SetFriendRelays { pub friend_public_key: PublicKey, pub relays: Vec>, } -#[capnp_conv(crate::app_server_capnp::reset_friend_channel)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ResetFriendChannel { pub friend_public_key: PublicKey, pub reset_token: Signature, } -#[capnp_conv(crate::app_server_capnp::set_friend_currency_rate)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct SetFriendCurrencyRate { pub friend_public_key: PublicKey, @@ -743,7 +718,6 @@ pub struct SetFriendCurrencyRate { pub rate: Rate, } -#[capnp_conv(crate::app_server_capnp::remove_friend_currency)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct RemoveFriendCurrency { pub friend_public_key: PublicKey, @@ -753,11 +727,10 @@ pub struct RemoveFriendCurrency { /// A friend's route with known capacity #[derive(Debug, Clone, PartialEq, Eq)] struct FriendsRouteCapacity { - route: FriendsRoute, + route: Vec, capacity: u128, } -#[capnp_conv(crate::app_server_capnp::receipt_ack)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ReceiptAck { pub request_id: Uid, @@ -765,7 +738,6 @@ pub struct ReceiptAck { } /// Start a payment, possibly by paying through multiple routes. -#[capnp_conv(crate::app_server_capnp::create_payment)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct CreatePayment { /// payment_id is a randomly generated value (by the user), allowing the user to refer to a @@ -773,13 +745,11 @@ pub struct CreatePayment { pub payment_id: PaymentId, pub invoice_id: InvoiceId, pub currency: Currency, - #[capnp_conv(with = Wrapper)] pub total_dest_payment: u128, pub dest_public_key: PublicKey, } /// Start a payment, possibly by paying through multiple routes. -#[capnp_conv(crate::app_server_capnp::create_transaction)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct CreateTransaction { /// A payment id of an existing payment. @@ -787,15 +757,12 @@ pub struct CreateTransaction { /// Randomly generated request_id (by the user), /// allows the user to refer to this request later. pub request_id: Uid, - pub route: FriendsRoute, - #[capnp_conv(with = Wrapper)] + pub route: Vec, pub dest_payment: u128, - #[capnp_conv(with = Wrapper)] pub fees: u128, } /// Start an invoice (A request for payment). -#[capnp_conv(crate::app_server_capnp::add_invoice)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct AddInvoice { /// Randomly generated invoice_id, allows to refer to this invoice. @@ -803,12 +770,10 @@ pub struct AddInvoice { /// Currency in use pub currency: Currency, /// Total amount of credits to be paid. - #[capnp_conv(with = Wrapper)] pub total_dest_payment: u128, } /// Start an invoice (A request for payment). -#[capnp_conv(crate::app_server_capnp::ack_close_payment)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct AckClosePayment { pub payment_id: PaymentId, @@ -838,7 +803,7 @@ pub enum FunderControl { // Seller API: AddInvoice(AddInvoice), CancelInvoice(InvoiceId), - CommitInvoice(Commit), + // CommitInvoice(Commit), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -857,31 +822,27 @@ impl FunderIncomingControl { } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::app_server_capnp::request_result)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum RequestResult { - Complete(Commit), + // Complete(Commit), Success, // TODO: Should we add more information to the failure here? Failure, } -#[capnp_conv(crate::app_server_capnp::transaction_result)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct TransactionResult { pub request_id: Uid, pub result: RequestResult, } -#[capnp_conv(crate::app_server_capnp::payment_status_success)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct PaymentStatusSuccess { - pub receipt: Receipt, + // pub receipt: Receipt, pub ack_uid: Uid, } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::app_server_capnp::payment_status)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum PaymentStatus { PaymentNotFound, @@ -889,7 +850,6 @@ pub enum PaymentStatus { Canceled(Uid), // ack_id } -#[capnp_conv(crate::app_server_capnp::response_close_payment)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ResponseClosePayment { pub payment_id: PaymentId, @@ -898,10 +858,10 @@ pub struct ResponseClosePayment { #[allow(clippy::large_enum_variant)] #[derive(Debug)] -pub enum FunderOutgoingControl { +pub enum FunderOutgoingControl { TransactionResult(TransactionResult), ResponseClosePayment(ResponseClosePayment), - ReportMutations(FunderReportMutations), + // ReportMutations(FunderReportMutations), } impl Currency { @@ -938,6 +898,7 @@ impl FromStr for Currency { } } +/* impl BalanceInfo { fn flip(self) -> BalanceInfo { BalanceInfo { @@ -947,9 +908,25 @@ impl BalanceInfo { } } } +*/ -impl McInfo { +/* +impl McBalance { pub fn flip(self) -> McInfo { + Self { + balance: self.balance.checked_neg().unwrap(), + local_pending_debt: self.remote_pending_debt, + remote_pending_debt: self.local_pending_debt, + in_fees: self.out_fees, + out_fees: self.in_fees, + } + } +} +*/ + +/* +impl TokenInfo { + pub fn flip(self) -> TokenInfo { let balances = self .balances .into_iter() @@ -958,62 +935,12 @@ impl McInfo { balance_info: currency_balance_info.balance_info.flip(), }) .collect(); - - McInfo { + TokenInfo { local_public_key: self.remote_public_key, remote_public_key: self.local_public_key, balances, + move_token_counter: self.move_token_counter, } } } - -impl TokenInfo { - pub fn flip(self) -> TokenInfo { - TokenInfo { - mc: self.mc.flip(), - counters: self.counters, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_friends_is_route_valid() { - assert_eq!(is_route_valid(&[1]), false); // too short - assert_eq!(is_route_part_valid(&[1]), true); // long enough - assert_eq!(is_route_valid(&Vec::::new()), false); // empty route is invalid - assert_eq!(is_route_part_valid(&Vec::::new()), true); // partial routes may be empty - - // Test cases taken from https://github.com/freedomlayer/offset/pull/215#discussion_r292327613 - assert_eq!(is_route_valid(&[1, 2, 3, 4]), true); // usual route - assert_eq!(is_route_valid(&[1, 2, 3, 4, 1]), true); // cyclic route that is at least 3 nodes long, having first item equal the last item - assert_eq!(is_route_valid(&[1, 1]), false); // cyclic route that is too short (only 2 nodes long) - assert_eq!(is_route_valid(&[1, 2, 3, 2, 4]), false); // Should have no repetitions that are not the first and last nodes. - - assert_eq!(is_route_part_valid(&[1, 2, 3, 4]), true); // usual route - assert_eq!(is_route_part_valid(&[1, 2, 3, 4, 1]), false); // should have no cycles in a partial route - assert_eq!(is_route_part_valid(&[1, 1]), false); // should have no repetitions ins a partial route - assert_eq!(is_route_part_valid(&[1, 2, 3, 2, 4]), false); // should have no repetitions in a partial route - } - - use im::hashset::HashSet as ImHashSet; - - #[derive(Arbitrary, Clone)] - struct ExampleHashSet { - my_set: ImHashSet, - } - - /* - // Does not compile, see: - // https://github.com/bodil/im-rs/issues/118 - use std::collections::HashMap as ImHashMap; - - #[derive(Arbitrary, Clone)] - struct ExampleHashMap { - my_map: ImHashMap, - } - */ -} +*/ diff --git a/components/proto/src/index_client/messages.rs b/components/proto/src/index_client/messages.rs index ca21276cb..4c59dbfb1 100644 --- a/components/proto/src/index_client/messages.rs +++ b/components/proto/src/index_client/messages.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::crypto::{PublicKey, Uid}; use crate::funder::messages::{Currency, Rate}; pub use crate::index_server::messages::{ @@ -27,7 +25,6 @@ pub struct IndexClientState { // IndexClient <--> AppServer communication // --------------------------------------------------- -#[capnp_conv(crate::report_capnp::index_client_report::opt_connected_server)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum OptConnectedServer { PublicKey(PublicKey), @@ -53,16 +50,16 @@ impl From for Option { } } -#[capnp_conv(crate::report_capnp::index_client_report)] +/* #[derive(Debug, Clone, PartialEq, Eq)] /// ISA stands for Index Server Address pub struct IndexClientReport { /// A list of trusted index servers. pub index_servers: Vec>, /// The server we are currently connected to (None if not connected). - #[capnp_conv(with = OptConnectedServer)] pub opt_connected_server: Option, } +*/ #[derive(Debug, Clone, PartialEq, Eq)] pub struct AddIndexServer { @@ -71,7 +68,6 @@ pub struct AddIndexServer { pub name: String, } -#[capnp_conv(crate::report_capnp::index_client_report_mutation::set_connected_server)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum SetConnectedServer { PublicKey(PublicKey), @@ -97,23 +93,19 @@ impl From for Option { } } -#[capnp_conv(crate::report_capnp::index_client_report_mutation)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum IndexClientReportMutation { AddIndexServer(NamedIndexServerAddress), RemoveIndexServer(PublicKey), - #[capnp_conv(with = SetConnectedServer)] SetConnectedServer(Option), } -#[capnp_conv(crate::app_server_capnp::response_routes_result)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum ResponseRoutesResult { Success(Vec), Failure, } -#[capnp_conv(crate::app_server_capnp::client_response_routes)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ClientResponseRoutes { pub request_id: Uid, @@ -145,6 +137,7 @@ pub enum AppServerToIndexClient { ApplyMutations(Vec), } +/* // TODO: Move this code somewhere else? impl IndexClientReport where @@ -172,3 +165,4 @@ where } } } +*/ diff --git a/components/proto/src/index_server/messages.rs b/components/proto/src/index_server/messages.rs index 86ef34159..36cde1dc0 100644 --- a/components/proto/src/index_server/messages.rs +++ b/components/proto/src/index_server/messages.rs @@ -1,22 +1,17 @@ use serde::{Deserialize, Serialize}; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use common::ser_utils::{ser_b64, ser_string}; use crate::crypto::{HashResult, PublicKey, RandValue, Signature, Uid}; -use crate::funder::messages::{Currency, FriendsRoute, Rate}; +use crate::funder::messages::{Currency, Rate}; use crate::net::messages::NetAddress; -use crate::wrapper::Wrapper; -#[capnp_conv(crate::index_capnp::edge)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct Edge { pub from_public_key: PublicKey, pub to_public_key: PublicKey, } -#[capnp_conv(crate::index_capnp::request_routes::opt_exclude)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum OptExclude { Empty, @@ -43,29 +38,24 @@ impl From for Option { } /// IndexClient -> IndexServer -#[capnp_conv(crate::index_capnp::request_routes)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct RequestRoutes { pub request_id: Uid, pub currency: Currency, /// Wanted capacity for the route. /// 0 means we want to optimize for capacity?? - #[capnp_conv(with = Wrapper)] pub capacity: u128, pub source: PublicKey, pub destination: PublicKey, /// This directed edge must not show up any any route inside the multi-route. /// Useful for finding non trivial directed loops. - #[capnp_conv(with = OptExclude)] pub opt_exclude: Option, } -#[capnp_conv(crate::index_capnp::route_capacity_rate)] #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct RouteCapacityRate { - pub route: FriendsRoute, + pub route: Vec, /// How many credits we can push along this route? - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub capacity: u128, /// Combined rate of pushing credits along this route. @@ -74,14 +64,12 @@ pub struct RouteCapacityRate { /// Multiple routes that together allow to pass a certain amount of credits to a destination. /// All routes must have the same beginning and the same end. -#[capnp_conv(crate::index_capnp::multi_route)] #[derive(Arbitrary, Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct MultiRoute { pub routes: Vec, } /// IndexServer -> IndexClient -#[capnp_conv(crate::index_capnp::response_routes)] #[derive(Debug, Clone)] pub struct ResponseRoutes { pub request_id: Uid, @@ -91,15 +79,15 @@ pub struct ResponseRoutes { } // TODO: Possibly think of a better name for this structure? -#[capnp_conv(crate::index_capnp::update_friend_currency)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct UpdateFriendCurrency { /// Friend's public key pub public_key: PublicKey, /// Currency being updated pub currency: Currency, - /// To denote local requests closed, assign 0 to recvCapacity - #[capnp_conv(with = Wrapper)] + /// Maximum amount of credit that can be pushed from us to remote friend. + pub send_capacity: u128, + /// Maximum amount of credit that can be pushed from remote friend to us. pub recv_capacity: u128, /// The rate we charge for forwarding messages to another friend from this friend. /// For example, in the following diagram we are X and A is the friend we are updating: @@ -112,7 +100,6 @@ pub struct UpdateFriendCurrency { } // TODO: Possibly think of a better name for this structure? -#[capnp_conv(crate::index_capnp::remove_friend_currency)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct RemoveFriendCurrency { /// Friend's public key @@ -122,14 +109,12 @@ pub struct RemoveFriendCurrency { } /// IndexClient -> IndexServer -#[capnp_conv(crate::index_capnp::index_mutation)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum IndexMutation { UpdateFriendCurrency(UpdateFriendCurrency), RemoveFriendCurrency(RemoveFriendCurrency), } -#[capnp_conv(crate::index_capnp::mutations_update)] #[derive(Debug, Clone)] pub struct MutationsUpdate { /// Public key of the node sending the mutations. @@ -156,7 +141,6 @@ pub struct MutationsUpdate { pub signature: Signature, } -#[capnp_conv(crate::index_capnp::time_proof_link)] #[derive(Debug, Clone)] pub struct TimeProofLink { /// List of hashes that produce a certain hash @@ -164,7 +148,6 @@ pub struct TimeProofLink { pub hashes: Vec, } -#[capnp_conv(crate::index_capnp::forward_mutations_update)] #[derive(Debug, Clone)] pub struct ForwardMutationsUpdate { pub mutations_update: MutationsUpdate, @@ -176,21 +159,18 @@ pub struct ForwardMutationsUpdate { pub time_proof_chain: Vec, } -#[capnp_conv(crate::index_capnp::index_server_to_client)] #[derive(Debug)] pub enum IndexServerToClient { TimeHash(HashResult), ResponseRoutes(ResponseRoutes), } -#[capnp_conv(crate::index_capnp::index_client_to_server)] #[derive(Debug)] pub enum IndexClientToServer { MutationsUpdate(MutationsUpdate), RequestRoutes(RequestRoutes), } -#[capnp_conv(crate::index_capnp::index_server_to_server)] #[derive(Debug)] pub enum IndexServerToServer { TimeHash(HashResult), @@ -200,7 +180,6 @@ pub enum IndexServerToServer { // ---------------------------------------------- // ---------------------------------------------- -#[capnp_conv(crate::common_capnp::named_index_server_address)] #[derive(Arbitrary, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct NamedIndexServerAddress { diff --git a/components/proto/src/keepalive/messages.rs b/components/proto/src/keepalive/messages.rs index e7191bf68..63838a195 100644 --- a/components/proto/src/keepalive/messages.rs +++ b/components/proto/src/keepalive/messages.rs @@ -1,6 +1,3 @@ -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - -#[capnp_conv(crate::keepalive_capnp::ka_message)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum KaMessage { KeepAlive, diff --git a/components/proto/src/lib.rs b/components/proto/src/lib.rs index cabaee61c..eb281ae41 100644 --- a/components/proto/src/lib.rs +++ b/components/proto/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, @@ -13,8 +13,6 @@ extern crate quickcheck_derive; // Workaround for issue: https://github.com/rust-lang/rust/issues/64450 extern crate offset_mutual_from as mutual_from; -#[macro_use] -pub mod macros; pub mod app_server; pub mod consts; pub mod crypto; @@ -24,18 +22,7 @@ pub mod index_client; pub mod index_server; pub mod keepalive; pub mod net; -pub mod proto_ser; pub mod relay; -pub mod report; +// pub mod report; pub mod secure_channel; pub mod ser_string; -pub mod wrapper; - -include_schema!(report_capnp, "report_capnp"); -include_schema!(app_server_capnp, "app_server_capnp"); -include_schema!(common_capnp, "common_capnp"); -include_schema!(dh_capnp, "dh_capnp"); -include_schema!(relay_capnp, "relay_capnp"); -include_schema!(funder_capnp, "funder_capnp"); -include_schema!(keepalive_capnp, "keepalive_capnp"); -include_schema!(index_capnp, "index_capnp"); diff --git a/components/proto/src/macros.rs b/components/proto/src/macros.rs deleted file mode 100644 index 9eb50e73e..000000000 --- a/components/proto/src/macros.rs +++ /dev/null @@ -1,13 +0,0 @@ -/// Include a capnp schema -macro_rules! include_schema { - ($( $name:ident, $path:expr );*) => { - $( - #[allow(unused, clippy::all)] - pub mod $name { - include!(concat!(env!("OUT_DIR"), "/schema/", $path, ".rs")); - } - - // use self::$name::*; - )* - }; -} diff --git a/components/proto/src/net/messages.rs b/components/proto/src/net/messages.rs index b334b69cf..3901be4b5 100644 --- a/components/proto/src/net/messages.rs +++ b/components/proto/src/net/messages.rs @@ -7,11 +7,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use derive_more::Display; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::consts::MAX_NET_ADDRESS_LENGTH; -#[capnp_conv(crate::common_capnp::net_address)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Display)] #[display(fmt = "{}", address)] pub struct NetAddress { diff --git a/components/proto/src/proto_ser.rs b/components/proto/src/proto_ser.rs deleted file mode 100644 index 045b99872..000000000 --- a/components/proto/src/proto_ser.rs +++ /dev/null @@ -1,36 +0,0 @@ -use derive_more::From; - -use capnp_conv::{CapnpConvError, FromCapnpBytes, ToCapnpBytes}; - -#[derive(Debug, From)] -pub enum ProtoSerializeError { - CapnpConvError(CapnpConvError), -} - -pub trait ProtoSerialize { - /// Serialize a Rust struct into bytes using Capnp - fn proto_serialize(&self) -> Vec; -} - -pub trait ProtoDeserialize: Sized { - /// Deserialize a Rust struct from bytes using Capnp - fn proto_deserialize(bytes: &[u8]) -> Result; -} - -impl ProtoSerialize for T -where - T: ToCapnpBytes, -{ - fn proto_serialize(&self) -> Vec { - self.to_capnp_bytes() - } -} - -impl ProtoDeserialize for T -where - T: FromCapnpBytes, -{ - fn proto_deserialize(bytes: &[u8]) -> Result { - Ok(Self::from_capnp_bytes(bytes)?) - } -} diff --git a/components/proto/src/relay/messages.rs b/components/proto/src/relay/messages.rs index f81b10bc0..5b94693a9 100644 --- a/components/proto/src/relay/messages.rs +++ b/components/proto/src/relay/messages.rs @@ -1,8 +1,5 @@ -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::crypto::PublicKey; -#[capnp_conv(crate::relay_capnp::init_connection)] #[derive(Debug, PartialEq, Eq)] pub enum InitConnection { Listen, @@ -12,13 +9,11 @@ pub enum InitConnection { Connect(PublicKey), } -#[capnp_conv(crate::relay_capnp::reject_connection)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct RejectConnection { pub public_key: PublicKey, } -#[capnp_conv(crate::relay_capnp::incoming_connection)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct IncomingConnection { pub public_key: PublicKey, diff --git a/components/proto/src/report/messages.rs b/components/proto/src/report/messages.rs index 4b0a27812..67afbb750 100644 --- a/components/proto/src/report/messages.rs +++ b/components/proto/src/report/messages.rs @@ -8,8 +8,6 @@ use common::mutable_state::MutableState; use common::never::Never; use common::ser_utils::ser_string; -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::crypto::{HashResult, PublicKey, RandValue, Signature, Uid}; use crate::app_server::messages::{NamedRelayAddress, RelayAddress}; @@ -19,7 +17,6 @@ use crate::funder::messages::{ use crate::net::messages::NetAddress; use crate::wrapper::Wrapper; -#[capnp_conv(crate::report_capnp::move_token_hashed_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct MoveTokenHashedReport { pub prefix_hash: HashResult, @@ -28,39 +25,32 @@ pub struct MoveTokenHashedReport { pub new_token: Signature, } -#[capnp_conv(crate::report_capnp::friend_status_report)] #[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, Eq, PartialEq)] pub enum FriendStatusReport { Enabled, Disabled, } -#[capnp_conv(crate::report_capnp::requests_status_report)] #[derive(Arbitrary, Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] pub enum RequestsStatusReport { Open, Closed, } -#[capnp_conv(crate::report_capnp::mc_balance_report)] #[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct McBalanceReport { /// Amount of credits this side has against the remote side. /// The other side keeps the negation of this value. - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub balance: i128, /// Frozen credits by our side - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub local_pending_debt: u128, /// Frozen credits by the remote side - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub remote_pending_debt: u128, } -#[capnp_conv(crate::report_capnp::friend_liveness_report)] #[derive(Clone, Debug, Eq, PartialEq)] pub enum FriendLivenessReport { Online, @@ -77,21 +67,18 @@ impl FriendLivenessReport { } } -#[capnp_conv(crate::report_capnp::currency_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct CurrencyReport { pub currency: Currency, pub balance: McBalanceReport, } -#[capnp_conv(crate::report_capnp::reset_terms_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct ResetTermsReport { pub reset_token: Signature, pub balance_for_reset: Vec, } -#[capnp_conv(crate::report_capnp::channel_inconsistent_report::opt_remote_reset_terms)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum OptRemoteResetTerms { RemoteResetTerms(ResetTermsReport), @@ -117,21 +104,17 @@ impl From for Option { } } -#[capnp_conv(crate::report_capnp::channel_inconsistent_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct ChannelInconsistentReport { pub local_reset_terms: Vec, - #[capnp_conv(with = OptRemoteResetTerms)] pub opt_remote_reset_terms: Option, } -#[capnp_conv(crate::report_capnp::channel_consistent_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct ChannelConsistentReport { pub currency_reports: Vec, } -#[capnp_conv(crate::report_capnp::channel_status_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum ChannelStatusReport { Inconsistent(ChannelInconsistentReport), @@ -139,7 +122,6 @@ pub enum ChannelStatusReport { } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::report_capnp::opt_last_incoming_move_token)] #[derive(Clone, Debug, PartialEq, Eq)] pub enum OptLastIncomingMoveToken { MoveTokenHashed(MoveTokenHashedReport), @@ -169,7 +151,6 @@ impl From for Option { } } -#[capnp_conv(crate::report_capnp::currency_config_report)] #[derive(Arbitrary, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct CurrencyConfigReport { pub currency: Currency, @@ -177,14 +158,12 @@ pub struct CurrencyConfigReport { /// for a certain currency. pub rate: Rate, /// Credit frame for the remote side (Set by the user of this node) - #[capnp_conv(with = Wrapper)] #[serde(with = "ser_string")] pub remote_max_debt: u128, /// Can requests be sent through this mutual credit? pub is_open: bool, } -#[capnp_conv(crate::report_capnp::friend_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct FriendReport { pub name: String, @@ -192,7 +171,6 @@ pub struct FriendReport { pub currency_configs: Vec, // Last message signed by the remote side. // Can be used as a proof for the last known balance. - #[capnp_conv(with = OptLastIncomingMoveToken)] pub opt_last_incoming_move_token: Option, // TODO: The state of liveness = true with status = disabled should never happen. // Can we somehow express this in the type system? @@ -201,14 +179,12 @@ pub struct FriendReport { pub status: FriendStatusReport, } -#[capnp_conv(crate::report_capnp::pk_friend_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PkFriendReport { pub friend_public_key: PublicKey, pub friend_report: FriendReport, } -#[capnp_conv(crate::report_capnp::pk_friend_report_list)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct PkFriendReportList { list: Vec>, @@ -245,18 +221,15 @@ impl From>> for PkFriendReportList { /// A FunderReport is a summary of a FunderState. /// It contains the information the Funder exposes to the user apps of the Offset node. -#[capnp_conv(crate::report_capnp::funder_report)] #[derive(Debug, Clone, PartialEq, Eq)] // TODO: Removed A: Clone here and ImHashMap. Should this struct be cloneable for some reason? pub struct FunderReport { pub local_public_key: PublicKey, pub relays: Vec>, - #[capnp_conv(with = PkFriendReportList)] pub friends: HashMap>, } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::report_capnp::friend_report_mutation)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum FriendReportMutation { SetRemoteRelays(Vec>), @@ -265,23 +238,19 @@ pub enum FriendReportMutation { RemoveCurrencyConfig(Currency), SetChannelStatus(ChannelStatusReport), SetStatus(FriendStatusReport), - #[capnp_conv(with = OptLastIncomingMoveToken)] SetOptLastIncomingMoveToken(Option), SetLiveness(FriendLivenessReport), } -#[capnp_conv(crate::report_capnp::add_friend_report)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct AddFriendReport { pub friend_public_key: PublicKey, pub name: String, pub relays: Vec>, - #[capnp_conv(with = OptLastIncomingMoveToken)] pub opt_last_incoming_move_token: Option, pub channel_status: ChannelStatusReport, } -#[capnp_conv(crate::report_capnp::pk_friend_report_mutation)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct PkFriendReportMutation { friend_public_key: PublicKey, @@ -306,14 +275,12 @@ impl From<(PublicKey, FriendReportMutation)> for PkFriendReportMutation { } #[allow(clippy::large_enum_variant)] -#[capnp_conv(crate::report_capnp::funder_report_mutation)] #[derive(Debug, Clone, PartialEq, Eq)] pub enum FunderReportMutation { AddRelay(NamedRelayAddress), RemoveRelay(PublicKey), AddFriend(AddFriendReport), RemoveFriend(PublicKey), - #[capnp_conv(with = PkFriendReportMutation)] PkFriendReportMutation((PublicKey, FriendReportMutation)), } diff --git a/components/proto/src/schema/app_server.capnp b/components/proto/src/schema/app_server.capnp index 59c025b91..3e5ad2fca 100644 --- a/components/proto/src/schema/app_server.capnp +++ b/components/proto/src/schema/app_server.capnp @@ -17,8 +17,8 @@ using import "common.capnp".NetAddress; using import "common.capnp".NamedIndexServerAddress; using import "common.capnp".Currency; -using import "report.capnp".NodeReport; -using import "report.capnp".NodeReportMutation; +# using import "report.capnp".NodeReport; +# using import "report.capnp".NodeReportMutation; using import "index.capnp".RequestRoutes; using import "index.capnp".MultiRoute; @@ -156,16 +156,16 @@ struct AppPermissions { } -struct ReportMutations { - optAppRequestId: union { - appRequestId @0: Uid; - # Mutations were caused by an application request. - empty @1: Void; - # Mutations were caused for some other reason. - } - mutations @2: List(NodeReportMutation); - # A list of mutations -} +# struct ReportMutations { +# optAppRequestId: union { +# appRequestId @0: Uid; +# # Mutations were caused by an application request. +# empty @1: Void; +# # Mutations were caused for some other reason. +# } +# mutations @2: List(NodeReportMutation); +# # A list of mutations +# } struct RequestResult { union { @@ -207,10 +207,10 @@ struct AppServerToApp { # Reports about current state: # report @2: NodeReport; - reportMutations @2: ReportMutations; + # reportMutations @2: ReportMutations; # Routes: - responseRoutes @3: ClientResponseRoutes; + responseRoutes @2: ClientResponseRoutes; } } diff --git a/components/proto/src/schema/common.capnp b/components/proto/src/schema/common.capnp index 4254349ed..c296fe8ac 100644 --- a/components/proto/src/schema/common.capnp +++ b/components/proto/src/schema/common.capnp @@ -57,6 +57,14 @@ struct HashResult { inner @0: Buffer256; } +struct HmacResult { + inner @0: Buffer256; +} + +struct HmacKey { + inner @0: Buffer256; +} + struct InvoiceId { inner @0: Buffer256; } @@ -73,10 +81,15 @@ struct PaymentId { inner @0: Buffer128; } +struct NodePort { + inner @0: Buffer128; +} + struct PlainLock { inner @0: Buffer256; } + struct HashedLock { inner @0: Buffer256; } @@ -103,6 +116,7 @@ struct Currency { struct RelayAddress { publicKey @0: PublicKey; address @1: NetAddress; + port @2: NodePort; } # Authenticated named address of a Relay (Includes public key) diff --git a/components/proto/src/schema/funder.capnp b/components/proto/src/schema/funder.capnp index b421f8d41..4bfdbb268 100644 --- a/components/proto/src/schema/funder.capnp +++ b/components/proto/src/schema/funder.capnp @@ -2,7 +2,6 @@ using import "common.capnp".Signature; using import "common.capnp".PublicKey; -using import "common.capnp".RandValue; using import "common.capnp".InvoiceId; using import "common.capnp".Uid; using import "common.capnp".CustomUInt128; @@ -11,6 +10,7 @@ using import "common.capnp".RelayAddress; using import "common.capnp".HashedLock; using import "common.capnp".PlainLock; using import "common.capnp".HashResult; +using import "common.capnp".HmacResult; using import "common.capnp".Currency; @@ -34,29 +34,12 @@ struct MoveToken { # Operations that should be applied to various currencies. # For every currency, ordered batched operations are provided. # First operation should be applied first. - optLocalRelays: union { - empty @2: Void; - # Nothing has changed - relays @3: List(RelayAddress); - # Set this exact list to be the list of relays - } - # Set the relays used by the sender of this MoveToken message. - # (Empty means no change happens). - optActiveCurrencies: union { - empty @4: Void; - # Nothing has changed - currencies @5: List(Currency); - # Set this exact list to be the list of currencies - } - # Add a list of active currencies. (empty means that no change happens) - # Note that this field only allows to add new currencies. - infoHash @6: HashResult; + currenciesDiff @2: List(Currency); + # Exclusive-Or difference between previous list of currencies and new list of currencies. + # Should be empty if nothing has changed. + infoHash @3: HashResult; # Current information about the channel that both sides implicitly agree upon. - randNonce @7: RandValue; - # A random nonce, generated by the sender. We have it because the - # sender is signing over this message, and we don't want him to be - # tricked into signing over something strange. - newToken @8 : Signature; + newToken @4 : Signature; # A signature over all the previous fields. } @@ -73,17 +56,26 @@ struct CurrencyBalance { struct ResetTerms { resetToken @0: Signature; - inconsistencyCounter @1: UInt64; + moveTokenCounter @1: CustomUInt128; + # Newly proposed moveTokenCounter. Must be larger than both side's + # previous moveTokenCounter. balanceForReset @2: List(CurrencyBalance); # List of expected balance for each currency } +struct RelaysUpdate { + generation @0: CustomUInt128; + relays @1: List(RelayAddress); +} + # A message sent between friends. struct FriendMessage { union { moveTokenRequest @0: MoveTokenRequest; inconsistencyError @1: ResetTerms; + relaysUpdate @2: RelaysUpdate; + relaysAck @3: Uid; } } @@ -107,9 +99,15 @@ struct RequestSendFundsOp { route @2: FriendsRoute; destPayment @3: CustomUInt128; totalDestPayment @4: CustomUInt128; - invoiceId @5: InvoiceId; - # Id number of the invoice we are attempting to pay - leftFees @6: CustomUInt128; + invoiceHash @5: HashResult; + # A hash of the contents of the invoice (Including text etc) + # Possibly a hash over a random invoiceId, and text hash + hmac @6: HmacResult; + # An HMAC signature over the whole message, not including "leftFees" + # and "route" (as they change when the message is passed). + # The shared secret for the HMAC algorithm was received through + # "direct" relay communication with the seller. + leftFees @7: CustomUInt128; # Amount of fees left to give to mediators # Every mediator takes the amount of fees he wants and subtracts this # value accordingly. @@ -117,23 +115,18 @@ struct RequestSendFundsOp { struct ResponseSendFundsOp { requestId @0: Uid; - destHashedLock @1: HashedLock; - isComplete @2: Bool; - # Has the destination received all the funds he asked for at the invoice? - # Mostly meaningful in the case of multi-path payments. - # isComplete == True means that no more requests should be sent. - # The isComplete field is crucial for the construction of a Commit message. - randNonce @3: RandValue; - signature @4: Signature; + srcPlainLock @1: PlainLock; + serialNum @2: CustomUInt128; + # Serial number used for this collection of invoice money. + # This should be a u128 counter, increased by 1 for every collected + # invoice. + signature @3: Signature; # Signature{key=destinationKey}( # sha512/256("FUNDS_RESPONSE") || - # sha512/256(requestId || randNonce) || - # srcHashedLock || - # destHashedLock || - # isComplete || - # destPayment || + # sha512/256(requestId || hmac || srcPlainLock || destPayment) + # serialNum || # totalDestPayment || - # invoiceId || + # invoiceHash || # currency [Implicitly known by the mutual credit] # ) } @@ -142,18 +135,11 @@ struct CancelSendFundsOp { requestId @0: Uid; } -struct CollectSendFundsOp { - requestId @0: Uid; - srcPlainLock @1: PlainLock; - destPlainLock @2: PlainLock; -} - struct FriendTcOp { union { requestSendFunds @0: RequestSendFundsOp; responseSendFunds @1: ResponseSendFundsOp; cancelSendFunds @2: CancelSendFundsOp; - collectSendFunds @3: CollectSendFundsOp; } } diff --git a/components/proto/src/schema/report.capnp b/components/proto/src/schema/report.capnp index 5b5973c22..2c3e92638 100644 --- a/components/proto/src/schema/report.capnp +++ b/components/proto/src/schema/report.capnp @@ -39,6 +39,7 @@ struct McInfo { balances @2: List(CurrencyBalanceInfo); } + struct TokenInfo { mc @0: McInfo; counters @1: CountersInfo; diff --git a/components/proto/src/secure_channel/messages.rs b/components/proto/src/secure_channel/messages.rs index 0f11de1de..d2ee8e5ac 100644 --- a/components/proto/src/secure_channel/messages.rs +++ b/components/proto/src/secure_channel/messages.rs @@ -1,8 +1,5 @@ -use capnp_conv::{capnp_conv, CapnpConvError, ReadCapnp, WriteCapnp}; - use crate::crypto::{DhPublicKey, PublicKey, RandValue, Salt, Signature}; -#[capnp_conv(crate::dh_capnp::exchange_rand_nonce::opt_dest_public_key)] #[derive(Debug, PartialEq, Eq)] enum OptDestPublicKey { Empty, @@ -28,17 +25,14 @@ impl From for Option { } /// First Diffie-Hellman message: -#[capnp_conv(crate::dh_capnp::exchange_rand_nonce)] #[derive(Debug, PartialEq, Eq)] pub struct ExchangeRandNonce { pub rand_nonce: RandValue, pub src_public_key: PublicKey, - #[capnp_conv(with = OptDestPublicKey)] pub opt_dest_public_key: Option, } /// Second Diffie-Hellman message: -#[capnp_conv(crate::dh_capnp::exchange_dh)] #[derive(Debug, PartialEq, Eq)] pub struct ExchangeDh { pub dh_public_key: DhPublicKey, @@ -57,21 +51,18 @@ impl ExchangeDh { } } -#[capnp_conv(crate::dh_capnp::rekey)] #[derive(Debug, PartialEq, Eq, Clone)] pub struct Rekey { pub dh_public_key: DhPublicKey, pub key_salt: Salt, } -#[capnp_conv(crate::dh_capnp::channel_content)] #[derive(Debug, PartialEq, Eq, Clone)] pub enum ChannelContent { Rekey(Rekey), User(Vec), } -#[capnp_conv(crate::dh_capnp::channel_message)] #[derive(Debug, PartialEq, Eq)] pub struct ChannelMessage { pub rand_padding: Vec, diff --git a/components/proto/src/wrapper.rs b/components/proto/src/wrapper.rs deleted file mode 100644 index 3fea306cc..000000000 --- a/components/proto/src/wrapper.rs +++ /dev/null @@ -1,105 +0,0 @@ -use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; - -use serde::{Deserialize, Serialize}; - -use capnp_conv::{CapnpConvError, ReadCapnp, WriteCapnp}; - -#[derive( - derive_more::Constructor, - derive_more::From, - Serialize, - Deserialize, - Debug, - Clone, - Copy, - PartialEq, - Eq, -)] -pub struct Wrapper(T); - -impl std::ops::Deref for Wrapper { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Into for Wrapper { - fn into(self) -> u128 { - self.0 - } -} - -impl Into for Wrapper { - fn into(self) -> i128 { - self.0 - } -} - -impl<'a> WriteCapnp<'a> for Wrapper { - type WriterType = crate::common_capnp::custom_u_int128::Builder<'a>; - - fn write_capnp(&self, writer: &mut Self::WriterType) { - let mut inner = writer.reborrow().get_inner().unwrap(); - - let mut data_bytes = Vec::new(); - data_bytes.write_u128::(**self).unwrap(); - let mut cursor = std::io::Cursor::new(AsRef::<[u8]>::as_ref(&data_bytes)); - - inner.set_x0(cursor.read_u64::().unwrap()); - inner.set_x1(cursor.read_u64::().unwrap()); - } -} - -impl<'a> ReadCapnp<'a> for Wrapper { - type ReaderType = crate::common_capnp::custom_u_int128::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - let inner = reader.get_inner()?; - let mut vec = Vec::new(); - vec.write_u64::(inner.get_x0())?; - vec.write_u64::(inner.get_x1())?; - Ok(Wrapper::new(BigEndian::read_u128(&vec[..]))) - } -} - -impl<'a> WriteCapnp<'a> for Wrapper { - type WriterType = crate::common_capnp::custom_int128::Builder<'a>; - - fn write_capnp(&self, writer: &mut Self::WriterType) { - let mut inner = writer.reborrow().get_inner().unwrap(); - - let mut data_bytes = Vec::new(); - data_bytes.write_i128::(**self).unwrap(); - let mut cursor = std::io::Cursor::new(AsRef::<[u8]>::as_ref(&data_bytes)); - - inner.set_x0(cursor.read_u64::().unwrap()); - inner.set_x1(cursor.read_u64::().unwrap()); - } -} - -impl<'a> ReadCapnp<'a> for Wrapper { - type ReaderType = crate::common_capnp::custom_int128::Reader<'a>; - - fn read_capnp(reader: &Self::ReaderType) -> Result { - let inner = reader.get_inner()?; - let mut vec = Vec::new(); - vec.write_u64::(inner.get_x0())?; - vec.write_u64::(inner.get_x1())?; - Ok(Wrapper::new(BigEndian::read_i128(&vec[..]))) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn test_convert_wrapper() { - let _wrapper: Wrapper = 1i128.into(); - let _signed: i128 = Wrapper(1i128).into(); - let _signed: i128 = *Wrapper(1i128); - } -} diff --git a/components/relay/src/lib.rs b/components/relay/src/lib.rs index 8880fb871..9d05a702e 100644 --- a/components/relay/src/lib.rs +++ b/components/relay/src/lib.rs @@ -1,7 +1,7 @@ #![crate_type = "lib"] #![type_length_limit = "291239"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/route/src/lib.rs b/components/route/src/lib.rs index 742bc0dc3..35e7c1529 100644 --- a/components/route/src/lib.rs +++ b/components/route/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/secure_channel/src/lib.rs b/components/secure_channel/src/lib.rs index bc5894cca..c14c82b38 100644 --- a/components/secure_channel/src/lib.rs +++ b/components/secure_channel/src/lib.rs @@ -1,7 +1,7 @@ #![crate_type = "lib"] #![type_length_limit = "2097152"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/secure_channel/src/state.rs b/components/secure_channel/src/state.rs index 0e6404aac..4a583f744 100644 --- a/components/secure_channel/src/state.rs +++ b/components/secure_channel/src/state.rs @@ -4,7 +4,7 @@ use byteorder::{BigEndian, ByteOrder}; use derive_more::From; -use crypto::dh::DhPrivateKey; +use crypto::dh::DhEphemeralPrivateKey; use crypto::identity::verify_signature; use crypto::rand::{CryptoRandom, RandGen}; use crypto::sym_encrypt::{Decryptor, Encryptor}; @@ -48,12 +48,12 @@ pub struct ScStateHalf { pub remote_public_key: PublicKey, local_public_key: PublicKey, local_rand_nonce: RandValue, - dh_private_key: DhPrivateKey, + dh_private_key: DhEphemeralPrivateKey, local_salt: Salt, } struct PendingRekey { - local_dh_private_key: DhPrivateKey, + local_dh_private_key: DhEphemeralPrivateKey, local_salt: Salt, } @@ -105,7 +105,7 @@ impl ScStateInitial { } let dh_private_key = - DhPrivateKey::new(&mut rng).map_err(|_| ScStateError::PrivateKeyGenFailure)?; + DhEphemeralPrivateKey::new(&mut rng).map_err(|_| ScStateError::PrivateKeyGenFailure)?; let dh_public_key = dh_private_key .compute_public_key() .map_err(|_| ScStateError::DhPublicKeyComputeFailure)?; @@ -257,7 +257,7 @@ impl ScState { if self.opt_pending_rekey.is_some() { return Err(ScStateError::RekeyInProgress); } - let dh_private_key = DhPrivateKey::new(rng).unwrap(); + let dh_private_key = DhEphemeralPrivateKey::new(rng).unwrap(); let local_salt = Salt::rand_gen(rng); let dh_public_key = dh_private_key.compute_public_key().unwrap(); let pending_rekey = PendingRekey { @@ -280,7 +280,7 @@ impl ScState { ) -> Result { match self.opt_pending_rekey.take() { None => { - let dh_private_key = DhPrivateKey::new(rng).unwrap(); + let dh_private_key = DhEphemeralPrivateKey::new(rng).unwrap(); let local_salt = Salt::rand_gen(rng); let dh_public_key = dh_private_key.compute_public_key().unwrap(); diff --git a/components/signature/src/canonical.rs b/components/signature/src/canonical.rs index ae82c7951..2af0388a2 100644 --- a/components/signature/src/canonical.rs +++ b/components/signature/src/canonical.rs @@ -2,11 +2,8 @@ use byteorder::{BigEndian, WriteBytesExt}; // use std::collections::HashMap; use proto::app_server::messages::RelayAddress; -use proto::funder::messages::{ - BalanceInfo, CancelSendFundsOp, CollectSendFundsOp, CountersInfo, Currency, - CurrencyBalanceInfo, CurrencyOperations, FriendTcOp, FriendsRoute, McInfo, OptLocalRelays, - Receipt, RequestSendFundsOp, ResponseSendFundsOp, TokenInfo, -}; +use proto::crypto::PublicKey; +use proto::funder::messages::{Currency, McBalance}; use proto::index_server::messages::{IndexMutation, RemoveFriendCurrency, UpdateFriendCurrency}; use proto::net::messages::NetAddress; @@ -56,6 +53,16 @@ where } } +// TODO: Possibly be more generic here? We might be able to impl CanonicalSerialize for all fixed +// sized bytes types we have, if required. Not sure that we want to do this, though. +impl CanonicalSerialize for PublicKey { + fn canonical_serialize(&self) -> Vec { + let mut res_bytes = Vec::new(); + res_bytes.extend_from_slice(self); + res_bytes + } +} + impl CanonicalSerialize for String { fn canonical_serialize(&self) -> Vec { self.as_bytes().to_vec() @@ -144,101 +151,7 @@ impl CanonicalSerialize for Currency { } } -impl CanonicalSerialize for CurrencyOperations { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.currency.canonical_serialize()); - res_bytes.extend_from_slice(&self.operations.canonical_serialize()); - res_bytes - } -} - -impl CanonicalSerialize for RequestSendFundsOp { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.request_id); - res_bytes.extend_from_slice(&self.src_hashed_lock); - res_bytes.extend_from_slice(&self.route.canonical_serialize()); - res_bytes - .write_u128::(self.dest_payment) - .unwrap(); - res_bytes - .write_u128::(self.total_dest_payment) - .unwrap(); - res_bytes.extend_from_slice(&self.invoice_id); - res_bytes.write_u128::(self.left_fees).unwrap(); - res_bytes - } -} - -impl CanonicalSerialize for ResponseSendFundsOp { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.request_id); - res_bytes.extend_from_slice(&self.dest_hashed_lock); - res_bytes.extend_from_slice(&self.is_complete.canonical_serialize()); - res_bytes.extend_from_slice(&self.rand_nonce); - res_bytes.extend_from_slice(&self.signature); - res_bytes - } -} - -impl CanonicalSerialize for CancelSendFundsOp { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.request_id); - res_bytes - } -} - -impl CanonicalSerialize for CollectSendFundsOp { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.request_id); - res_bytes.extend_from_slice(&self.src_plain_lock); - res_bytes.extend_from_slice(&self.dest_plain_lock); - res_bytes - } -} - -impl CanonicalSerialize for FriendTcOp { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - match self { - FriendTcOp::RequestSendFunds(request_send_funds) => { - res_bytes.push(0u8); - res_bytes.append(&mut request_send_funds.canonical_serialize()) - } - FriendTcOp::ResponseSendFunds(response_send_funds) => { - res_bytes.push(1u8); - res_bytes.append(&mut response_send_funds.canonical_serialize()) - } - FriendTcOp::CancelSendFunds(cancel_send_funds) => { - res_bytes.push(2u8); - res_bytes.append(&mut cancel_send_funds.canonical_serialize()) - } - FriendTcOp::CollectSendFunds(commit_send_funds) => { - res_bytes.push(3u8); - res_bytes.append(&mut commit_send_funds.canonical_serialize()) - } - } - res_bytes - } -} - -impl CanonicalSerialize for FriendsRoute { - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - res_bytes - .write_u64::(usize_to_u64(self.public_keys.len()).unwrap()) - .unwrap(); - for public_key in &self.public_keys { - res_bytes.extend_from_slice(public_key); - } - res_bytes - } -} - +/* impl CanonicalSerialize for Receipt { fn canonical_serialize(&self) -> Vec { let mut res_bytes = Vec::new(); @@ -251,6 +164,7 @@ impl CanonicalSerialize for Receipt { res_bytes } } +*/ impl CanonicalSerialize for NetAddress { fn canonical_serialize(&self) -> Vec { @@ -270,23 +184,6 @@ where } } -impl CanonicalSerialize for OptLocalRelays -where - B: CanonicalSerialize, -{ - fn canonical_serialize(&self) -> Vec { - let mut res_bytes = Vec::new(); - match self { - OptLocalRelays::Empty => res_bytes.push(0u8), - OptLocalRelays::Relays(relays) => { - res_bytes.push(1u8); - res_bytes.append(&mut relays.canonical_serialize()); - } - }; - res_bytes - } -} - impl CanonicalSerialize for UpdateFriendCurrency { fn canonical_serialize(&self) -> Vec { let mut res_bytes = Vec::new(); @@ -325,6 +222,7 @@ impl CanonicalSerialize for IndexMutation { } } +/* impl CanonicalSerialize for BalanceInfo { fn canonical_serialize(&self) -> Vec { let mut res_bytes = Vec::new(); @@ -343,7 +241,9 @@ impl CanonicalSerialize for CurrencyBalanceInfo { res_bytes } } +*/ +/* impl CanonicalSerialize for McInfo { fn canonical_serialize(&self) -> Vec { let mut res_bytes = Vec::new(); @@ -363,11 +263,40 @@ impl CanonicalSerialize for CountersInfo { } } +*/ + +impl CanonicalSerialize for McBalance { + fn canonical_serialize(&self) -> Vec { + let mut res_bytes = Vec::new(); + res_bytes.write_i128::(self.balance).unwrap(); + res_bytes + .write_u128::(self.local_pending_debt) + .unwrap(); + res_bytes + .write_u128::(self.remote_pending_debt) + .unwrap(); + + // Write in/out fees as big endian: + let mut temp_array = [0u8; 32]; + self.in_fees.to_big_endian(&mut temp_array); + res_bytes.extend_from_slice(&temp_array); + self.out_fees.to_big_endian(&mut temp_array); + res_bytes.extend_from_slice(&temp_array); + + res_bytes + } +} +/* impl CanonicalSerialize for TokenInfo { fn canonical_serialize(&self) -> Vec { let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&self.mc.canonical_serialize()); - res_bytes.extend_from_slice(&self.counters.canonical_serialize()); + res_bytes.extend_from_slice(&self.local_public_key); + res_bytes.extend_from_slice(&self.remote_public_key); + res_bytes.extend_from_slice(&self.balances_hash); + res_bytes + .write_u128::(self.move_token_counter) + .unwrap(); res_bytes } } +*/ diff --git a/components/signature/src/lib.rs b/components/signature/src/lib.rs index d1e16af75..97febad19 100644 --- a/components/signature/src/lib.rs +++ b/components/signature/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/signature/src/signature_buff.rs b/components/signature/src/signature_buff.rs index c7df95f47..1c42d979e 100644 --- a/components/signature/src/signature_buff.rs +++ b/components/signature/src/signature_buff.rs @@ -1,53 +1,61 @@ use byteorder::{BigEndian, WriteBytesExt}; -use crypto::hash::{self, sha_512_256}; - -use proto::crypto::HashResult; +use crypto::hash; use common::int_convert::usize_to_u64; -use crate::canonical::CanonicalSerialize; -use proto::funder::messages::{ - Currency, PendingTransaction, TokenInfo, UnsignedMoveToken, UnsignedResponseSendFundsOp, -}; +use proto::crypto::{HashResult, PlainLock, PublicKey, Uid}; +use proto::funder::messages::{Currency, MoveToken, TokenInfo}; use proto::index_server::messages::MutationsUpdate; -use proto::report::messages::MoveTokenHashedReport; + +use crate::canonical::CanonicalSerialize; pub const FUNDS_RESPONSE_PREFIX: &[u8] = b"FUND_RESPONSE"; pub const FUNDS_CANCEL_PREFIX: &[u8] = b"FUND_CANCEL"; +/* + pub request_id: Uid, + pub currency: Currency, + pub dest_payment: u128, + pub invoice_hash: HashResult, + pub src_plain_lock: PlainLock, + pub serial_num: u128, + +*/ + /// Create the buffer we sign over at the Response funds. /// Note that the signature is not just over the Response funds bytes. The signed buffer also /// contains information from the Request funds. -pub fn create_response_signature_buffer( +pub fn create_response_signature_buffer( + request_id: &Uid, currency: &Currency, - response_send_funds: RSF, - pending_transaction: &PendingTransaction, -) -> Vec -where - RSF: Into, -{ - let response_send_funds: UnsignedResponseSendFundsOp = response_send_funds.into(); + dest_payment: u128, + invoice_hash: &HashResult, + src_plain_lock: &PlainLock, + serial_num: u128, +) -> Vec { + /* + /// Signature{key=destinationKey}( + /// hash("FUNDS_RESPONSE") || + /// hash(request_id || src_plain_lock || dest_payment) || + /// hash(currency) || + /// serialNum || + /// invoiceHash) + /// ) + */ let mut sbuffer = Vec::new(); - sbuffer.extend_from_slice(&hash::sha_512_256(FUNDS_RESPONSE_PREFIX)); + sbuffer.extend_from_slice(&hash::hash_buffer(FUNDS_RESPONSE_PREFIX)); let mut inner_blob = Vec::new(); - inner_blob.extend_from_slice(&pending_transaction.request_id); - inner_blob.extend_from_slice(&response_send_funds.rand_nonce); + inner_blob.extend_from_slice(request_id); + inner_blob.extend_from_slice(src_plain_lock); + inner_blob.write_u128::(dest_payment).unwrap(); - sbuffer.extend_from_slice(&hash::sha_512_256(&inner_blob)); - sbuffer.extend_from_slice(&pending_transaction.src_hashed_lock); - sbuffer.extend_from_slice(&response_send_funds.dest_hashed_lock); - sbuffer.extend_from_slice(&response_send_funds.is_complete.canonical_serialize()); - sbuffer - .write_u128::(pending_transaction.dest_payment) - .unwrap(); - sbuffer - .write_u128::(pending_transaction.total_dest_payment) - .unwrap(); - sbuffer.extend_from_slice(&pending_transaction.invoice_id); - sbuffer.extend_from_slice(¤cy.canonical_serialize()); + sbuffer.extend_from_slice(&hash::hash_buffer(&inner_blob)); + sbuffer.extend_from_slice(&hash::hash_buffer(¤cy.canonical_serialize())); + sbuffer.write_u128::(serial_num).unwrap(); + sbuffer.extend_from_slice(invoice_hash); sbuffer } @@ -56,6 +64,7 @@ where // NEXT is used for hashing for the next move token funds. pub const TOKEN_NEXT: &[u8] = b"NEXT"; +/* /// Combine all operations into one hash value. pub fn operations_hash(move_token: MT) -> HashResult where @@ -65,7 +74,9 @@ where let operations_data = move_token.currencies_operations.canonical_serialize(); sha_512_256(&operations_data) } +*/ +/* pub fn local_address_hash(move_token: MT) -> HashResult where B: CanonicalSerialize + Clone, @@ -74,11 +85,27 @@ where let move_token: UnsignedMoveToken = move_token.into(); sha_512_256(&move_token.opt_local_relays.canonical_serialize()) } +*/ + +pub fn hash_token_info( + local_public_key: &PublicKey, + remote_public_key: &PublicKey, + token_info: &TokenInfo, +) -> HashResult { + let mut move_token_counter_buff = Vec::new(); + move_token_counter_buff + .write_u128::(token_info.move_token_counter) + .unwrap(); -pub fn hash_token_info(token_info: &TokenInfo) -> HashResult { - sha_512_256(&token_info.canonical_serialize()) + hash::Hasher::new() + .chain(&local_public_key) + .chain(&remote_public_key) + .chain(&token_info.balances_hash) + .chain(&move_token_counter_buff) + .finalize() } +/* /// Hash operations and local_address: pub fn prefix_hash(move_token: MT) -> HashResult where @@ -89,24 +116,37 @@ where let mut hash_buff = Vec::new(); hash_buff.extend_from_slice(&move_token.old_token); - hash_buff.extend_from_slice(&move_token.currencies_operations.canonical_serialize()); - hash_buff.extend_from_slice(&move_token.opt_local_relays.canonical_serialize()); - hash_buff.extend_from_slice(&move_token.opt_active_currencies.canonical_serialize()); + // hash_buff.extend_from_slice(&move_token.currencies_operations.canonical_serialize()); + // hash_buff.extend_from_slice(&move_token.opt_local_relays.canonical_serialize()); + //hash_buff.extend_from_slice(&move_token.opt_active_currencies.canonical_serialize()); sha_512_256(&hash_buff) } +*/ -pub fn move_token_signature_buff(move_token: MT) -> Vec -where - B: CanonicalSerialize + Clone, - MT: Into>, -{ - let move_token: UnsignedMoveToken = move_token.into(); +pub fn move_token_signature_buff(move_token: &MoveToken, info_hash: &HashResult) -> Vec { + let mut sig_buffer = Vec::new(); + sig_buffer.extend_from_slice(&hash::hash_buffer(TOKEN_NEXT)); + sig_buffer.extend_from_slice(&move_token.old_token); + sig_buffer.extend_from_slice(info_hash); + sig_buffer +} + +// TODO: Rethink string in this prefix: +pub const RESET_TOKEN_PREFIX: &[u8] = b"RESET_TOKEN"; + +pub fn reset_token_signature_buff( + local_public_key: &PublicKey, + remote_public_key: &PublicKey, + move_token_counter: u128, +) -> Vec { let mut sig_buffer = Vec::new(); - sig_buffer.extend_from_slice(&sha_512_256(TOKEN_NEXT)); - sig_buffer.extend_from_slice(&prefix_hash(move_token.clone())); - sig_buffer.extend_from_slice(&move_token.info_hash); - sig_buffer.extend_from_slice(&move_token.rand_nonce); + sig_buffer.extend_from_slice(&hash::hash_buffer(RESET_TOKEN_PREFIX)); + sig_buffer.extend_from_slice(&local_public_key); + sig_buffer.extend_from_slice(&remote_public_key); + sig_buffer + .write_u128::(move_token_counter) + .unwrap(); sig_buffer } @@ -114,7 +154,7 @@ pub const MUTATIONS_UPDATE_PREFIX: &[u8] = b"MUTATIONS_UPDATE"; pub fn create_mutations_update_signature_buff(mutations_update: &MutationsUpdate) -> Vec { let mut res_bytes = Vec::new(); - res_bytes.extend_from_slice(&hash::sha_512_256(MUTATIONS_UPDATE_PREFIX)); + res_bytes.extend_from_slice(&hash::hash_buffer(MUTATIONS_UPDATE_PREFIX)); res_bytes.extend_from_slice(&mutations_update.node_public_key); res_bytes @@ -133,14 +173,3 @@ pub fn create_mutations_update_signature_buff(mutations_update: &MutationsUpdate res_bytes } - -pub fn move_token_hashed_report_signature_buff( - move_token_hashed_report: &MoveTokenHashedReport, -) -> Vec { - let mut sig_buffer = Vec::new(); - sig_buffer.extend_from_slice(&sha_512_256(TOKEN_NEXT)); - sig_buffer.extend_from_slice(&move_token_hashed_report.prefix_hash); - sig_buffer.extend_from_slice(&hash_token_info(&move_token_hashed_report.token_info)); - sig_buffer.extend_from_slice(&move_token_hashed_report.rand_nonce); - sig_buffer -} diff --git a/components/signature/src/verify.rs b/components/signature/src/verify.rs index 1813ecf5a..7153f26f8 100644 --- a/components/signature/src/verify.rs +++ b/components/signature/src/verify.rs @@ -1,27 +1,27 @@ -use byteorder::{BigEndian, WriteBytesExt}; +// use byteorder::{BigEndian, WriteBytesExt}; -use crypto::hash; -use crypto::hash_lock::HashLock; +// use crypto::hash; +// use crypto::hash_lock::HashLock; use crypto::identity::verify_signature; -use proto::crypto::PublicKey; +use proto::crypto::{HashResult, PublicKey}; -use proto::funder::messages::{Commit, MoveToken, Receipt}; +use proto::funder::messages::MoveToken; use proto::index_server::messages::MutationsUpdate; -use proto::report::messages::MoveTokenHashedReport; -use crate::canonical::CanonicalSerialize; +// use crate::canonical::CanonicalSerialize; use crate::signature_buff::{ - create_mutations_update_signature_buff, move_token_hashed_report_signature_buff, - move_token_signature_buff, FUNDS_RESPONSE_PREFIX, + create_mutations_update_signature_buff, + move_token_signature_buff, /*, FUNDS_RESPONSE_PREFIX,*/ }; +/* // TODO: Add a local test that makes sure verify_receipt is in sync with verify_commit_signature /// Verify that a given receipt's signature is valid pub fn verify_receipt(receipt: &Receipt, public_key: &PublicKey) -> bool { let mut data = Vec::new(); - data.extend_from_slice(&hash::sha_512_256(FUNDS_RESPONSE_PREFIX)); + data.extend_from_slice(&hash::hash_buffer(FUNDS_RESPONSE_PREFIX)); data.extend(receipt.response_hash.as_ref()); data.extend_from_slice(&receipt.src_plain_lock.hash_lock()); data.extend_from_slice(&receipt.dest_plain_lock.hash_lock()); @@ -33,12 +33,14 @@ pub fn verify_receipt(receipt: &Receipt, public_key: &PublicKey) -> bool { data.extend_from_slice(&receipt.currency.canonical_serialize()); verify_signature(&data, public_key, &receipt.signature) } +*/ +/* /// Verify that a given Commit signature is valid fn verify_commit_signature(commit: &Commit, local_public_key: &PublicKey) -> bool { let mut data = Vec::new(); - data.extend_from_slice(&hash::sha_512_256(FUNDS_RESPONSE_PREFIX)); + data.extend_from_slice(&hash::hash_buffer(FUNDS_RESPONSE_PREFIX)); data.extend(commit.response_hash.as_ref()); data.extend_from_slice(&commit.src_plain_lock.hash_lock()); data.extend_from_slice(&commit.dest_hashed_lock); @@ -62,13 +64,15 @@ pub fn verify_commit(commit: &Commit, local_public_key: &PublicKey) -> bool { // Verify signature: verify_commit_signature(commit, local_public_key) } +*/ /// Verify that new_token is a valid signature over the rest of the fields. -pub fn verify_move_token(move_token: MoveToken, public_key: &PublicKey) -> bool -where - B: CanonicalSerialize + Clone, -{ - let sig_buffer = move_token_signature_buff(move_token.clone()); +pub fn verify_move_token( + move_token: &MoveToken, + info_hash: &HashResult, + public_key: &PublicKey, +) -> bool { + let sig_buffer = move_token_signature_buff(move_token, info_hash); verify_signature(&sig_buffer, public_key, &move_token.new_token) } @@ -83,14 +87,3 @@ pub fn verify_mutations_update(mutations_update: &MutationsUpdate) -> bool { &mutations_update.signature, ) } - -// TODO: Is the public_key argument redundant now? (As it should be exactly the same -// as move_token_hashed_report.local_public_key) -/// Verify that new_token is a valid signature over the rest of the fields. -pub fn verify_move_token_hashed_report( - move_token_hashed_report: &MoveTokenHashedReport, - public_key: &PublicKey, -) -> bool { - let sig_buffer = move_token_hashed_report_signature_buff(move_token_hashed_report); - verify_signature(&sig_buffer, public_key, &move_token_hashed_report.new_token) -} diff --git a/components/stcompact/Cargo.toml b/components/stcompact/Cargo.toml index b70c242e6..f11b6efe9 100644 --- a/components/stcompact/Cargo.toml +++ b/components/stcompact/Cargo.toml @@ -48,9 +48,9 @@ derive_more = "0.99.2" serde_json = "1.0.44" # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_macros = {version = "0.8"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" +quickcheck_macros = "0.9.1" rand = {version = "0.7.2"} [dev-dependencies] diff --git a/components/stcompact/src/bin/stcompact.rs b/components/stcompact/src/bin/stcompact.rs index 08e8ca738..508fa2231 100644 --- a/components/stcompact/src/bin/stcompact.rs +++ b/components/stcompact/src/bin/stcompact.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/stcompact/src/bin/stcompact_ser_gen.rs b/components/stcompact/src/bin/stcompact_ser_gen.rs index edcabd20e..12257a742 100644 --- a/components/stcompact/src/bin/stcompact_ser_gen.rs +++ b/components/stcompact/src/bin/stcompact_ser_gen.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/stcompact/src/lib.rs b/components/stcompact/src/lib.rs index 19ca69ba1..b2d43c15e 100644 --- a/components/stcompact/src/lib.rs +++ b/components/stcompact/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/stctrl/Cargo.toml b/components/stctrl/Cargo.toml index 65d9491b7..1a53c9dce 100644 --- a/components/stctrl/Cargo.toml +++ b/components/stctrl/Cargo.toml @@ -37,9 +37,9 @@ structopt = "0.2.15" derive_more = "0.14.0" # Quickcheck: -quickcheck = {version = "0.9"} -quickcheck_macros = {version = "0.8"} -quickcheck_derive = {version = "0.2.1"} +quickcheck = "0.9.2" +quickcheck_derive = "0.3" +quickcheck_macros = "0.9.1" rand = {version = "0.7.2"} [dev_dependencies] diff --git a/components/stctrl/src/bin/stctrl.rs b/components/stctrl/src/bin/stctrl.rs index 1938192a3..9bbeddf00 100644 --- a/components/stctrl/src/bin/stctrl.rs +++ b/components/stctrl/src/bin/stctrl.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/stctrl/src/bin/stverify.rs b/components/stctrl/src/bin/stverify.rs index bfb86c858..5c8dea23f 100644 --- a/components/stctrl/src/bin/stverify.rs +++ b/components/stctrl/src/bin/stverify.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/stctrl/src/lib.rs b/components/stctrl/src/lib.rs index c7606b665..01555ea5d 100644 --- a/components/stctrl/src/lib.rs +++ b/components/stctrl/src/lib.rs @@ -1,5 +1,5 @@ #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/timer/src/lib.rs b/components/timer/src/lib.rs index a0c84f010..436816bba 100644 --- a/components/timer/src/lib.rs +++ b/components/timer/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/components/timer/src/timer.rs b/components/timer/src/timer.rs index 2f90d4433..83c6a652d 100644 --- a/components/timer/src/timer.rs +++ b/components/timer/src/timer.rs @@ -20,7 +20,7 @@ //! [futures]: https://github.com/alexcrichton/futures //! [futures-timer]: https://github.com/alexcrichton/futures-timer -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow(clippy::implicit_hasher, clippy::module_inception)] use common::conn::BoxStream; diff --git a/components/version/src/lib.rs b/components/version/src/lib.rs index fcc706384..f4dbf8417 100644 --- a/components/version/src/lib.rs +++ b/components/version/src/lib.rs @@ -1,6 +1,6 @@ #![crate_type = "lib"] #![deny(trivial_numeric_casts, warnings)] -#![allow(intra_doc_link_resolution_failure)] +#![allow(broken_intra_doc_links)] #![allow( clippy::too_many_arguments, clippy::implicit_hasher, diff --git a/rust-toolchain b/rust-toolchain index 372cf402c..21998d3c2 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.44.0 +1.47.0