syncwhole

This commit is contained in:
Adam Ierymenko 2022-03-01 12:26:49 -05:00
parent 4ca9e49b4a
commit 27389825da
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
17 changed files with 1294 additions and 745 deletions

View file

@ -1,6 +1,6 @@
all:
clean: FORCE
rm -rf zerotier-core-crypto/target zerotier-network-hypervisor/target zerotier-system-service/target
rm -rf zerotier-core-crypto/target zerotier-network-hypervisor/target zerotier-system-service/target syncwhole/target aes-gmac-siv/target
FORCE:

702
egads/Cargo.lock generated
View file

@ -1,702 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aes-gmac-siv"
version = "0.1.0"
dependencies = [
"openssl",
]
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "atomic-polyfill"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee6adc1648f03fbc1bc1b5cf0f2fdfb5edbc96215b711edcfe6ce2641ef9b347"
dependencies = [
"critical-section",
"riscv-target",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version",
]
[[package]]
name = "bare-metal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cortex-m"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826"
dependencies = [
"bare-metal 0.2.5",
"bitfield",
"embedded-hal",
"volatile-register",
]
[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
[[package]]
name = "critical-section"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70"
dependencies = [
"bare-metal 1.0.0",
"cfg-if",
"cortex-m",
"riscv",
]
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
"digest",
"rand_core",
"subtle",
"zeroize",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "ed25519"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
dependencies = [
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand",
"serde",
"sha2",
"zeroize",
]
[[package]]
name = "egads"
version = "0.1.0"
dependencies = [
"rmp-serde",
"serde",
"zerotier-core-crypto",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]]
name = "heapless"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb"
dependencies = [
"atomic-polyfill",
"hash32",
"spin",
"stable_deref_trait",
]
[[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.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.0.0",
]
[[package]]
name = "nb"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-sys",
]
[[package]]
name = "openssl-sys"
version = "0.9.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "pkg-config"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "poly1305"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom",
"libc",
"rand_chacha",
"rand_core",
"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",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "riscv"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba"
dependencies = [
"bare-metal 1.0.0",
"bit_field",
"riscv-target",
]
[[package]]
name = "riscv-target"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
dependencies = [
"lazy_static",
"regex",
]
[[package]]
name = "rmp"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6"
dependencies = [
"byteorder",
"num-traits",
]
[[package]]
name = "rmp-serde"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
name = "signature"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4"
[[package]]
name = "spin"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
dependencies = [
"lock_api",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
dependencies = [
"vcell",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "x25519-dalek"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077"
dependencies = [
"curve25519-dalek",
"rand_core",
"zeroize",
]
[[package]]
name = "zeroize"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerotier-core-crypto"
version = "0.1.0"
dependencies = [
"aes-gmac-siv",
"ed25519-dalek",
"foreign-types",
"heapless",
"lazy_static",
"openssl",
"poly1305",
"rand_core",
"subtle",
"x25519-dalek",
]

View file

@ -1,9 +0,0 @@
[package]
name = "egads"
version = "0.1.0"
edition = "2021"
[dependencies]
zerotier-core-crypto = { path = "../zerotier-core-crypto" }
serde = { version = "^1", features = ["derive"], default-features = false }
rmp-serde = "^0"

View file

@ -1,3 +0,0 @@
EGADS: Embeddable Globally Authenticated Data Store
=====

View file

@ -1,8 +0,0 @@
pub struct Block<'a> {
pub validator: &'a [u8],
pub parent: &'a [u8],
pub timestamp: u64,
pub height: u64,
pub records: &'a [u8],
pub signature: &'a [u8],
}

View file

@ -1 +0,0 @@
mod block;

330
syncwhole/Cargo.lock generated Normal file
View file

@ -0,0 +1,330 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "libc"
version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "mio"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
dependencies = [
"bitflags",
]
[[package]]
name = "rmp"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6"
dependencies = [
"byteorder",
"num-traits",
]
[[package]]
name = "rmp-serde"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3eedffbfcc6a428f230c04baf8f59bd73c1781361e4286111fe900849aaddaf"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "syncwhole"
version = "0.1.0"
dependencies = [
"rmp",
"rmp-serde",
"serde",
"tokio",
]
[[package]]
name = "tokio"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"parking_lot",
"pin-project-lite",
"socket2",
"winapi",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"

12
syncwhole/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "syncwhole"
version = "0.1.0"
edition = "2021"
license = "MPL-2.0"
authors = ["Adam Ierymenko <adam.ierymenko@zerotier.com>"]
[dependencies]
tokio = { version = "^1", features = ["net", "rt", "parking_lot", "time", "io-std", "io-util", "sync"], default-features = false }
serde = { version = "^1", features = ["derive"], default-features = false }
rmp = "^0"
rmp-serde = "^1"

192
syncwhole/src/database.rs Normal file
View file

@ -0,0 +1,192 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::ops::Bound::Included;
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
/// Result returned by DB::load().
pub enum LoadResult<V: AsRef<[u8]> + Send> {
/// Object was found.
Ok(V),
/// Object was not found, including the case of being excluded due to the value of reference_time.
NotFound,
/// Supplied reference_time is outside what is available (usually too old).
TimeNotAvailable
}
/// Result returned by DB::store().
pub enum StoreResult {
/// Entry was accepted (whether or not an old value was replaced).
Ok,
/// Entry was rejected as a duplicate but was otherwise valid.
Duplicate,
/// Entry was rejected as invalid.
///
/// An invalid entry is one that is malformed, fails a signature check, etc., and returning
/// this causes the synchronization service to drop the link to the node that sent it.
Rejected
}
/// API to be implemented by the data store we want to replicate.
///
/// The API specified here supports temporally subjective data sets. These are data sets
/// where the existence or non-existence of a record may depend on the (real world) time.
/// A parameter for reference time allows a remote querying node to send its own "this is
/// what time I think it is" value to be considered locally so that data can be replicated
/// as of any given time.
///
/// The KEY_IS_COMPUTED constant must be set to indicate whether keys are a pure function of
/// values. If this is true, get_key() must be implemented.
///
/// The implementation must be thread safe.
pub trait Database: Sync + Send {
/// Type to be enclosed in the Ok() enum value in LoadResult.
type LoadResultValueType: AsRef<[u8]> + Send;
/// Size of keys, which must be fixed in length. These are typically hashes.
const KEY_SIZE: usize;
/// Maximum size of a value in bytes.
const MAX_VALUE_SIZE: usize;
/// This should be true if the key is computed, such as by hashing the value.
///
/// If this is true only values are sent over the wire and get_key() is used to compute
/// keys from values. If this is false both keys and values are replicated.
const KEY_IS_COMPUTED: bool;
/// Get the key corresponding to a value.
///
/// If KEY_IS_COMPUTED is true this must be implemented. The default implementation
/// panics to indicate this. If KEY_IS_COMPUTED is false this is never called.
#[allow(unused_variables)]
fn get_key(value: &[u8], key: &mut [u8]) {
panic!("get_key() must be implemented if KEY_IS_COMPUTED is true");
}
/// Get an item if it exists as of a given reference time.
///
/// The supplied key must be of length KEY_SIZE or this may panic.
fn load(&self, reference_time: i64, key: &[u8]) -> LoadResult<Self::LoadResultValueType>;
/// Store an item in the data store and return Ok, Duplicate, or Rejected.
///
/// The supplied key must be of length KEY_SIZE or this may panic.
///
/// Note that no time is supplied here. The data store must determine this in an implementation
/// dependent manner if this is a temporally subjective data store. It could be determined by
/// the wall clock, from the object itself, etc.
fn store(&self, key: &[u8], value: &[u8]) -> StoreResult;
/// Get the number of items under a prefix as of a given reference time.
///
/// The default implementation uses for_each(). This can be specialized if it can be done
/// more efficiently than that.
fn count(&self, reference_time: i64, key_prefix: &[u8]) -> u64 {
let mut cnt: u64 = 0;
self.for_each(reference_time, key_prefix, |_, _| {
cnt += 1;
true
});
cnt
}
/// Iterate through keys beneath a key prefix, stopping at the end or if the function returns false.
///
/// The default implementation uses for_each().
fn for_each_key<F: FnMut(&[u8]) -> bool>(&self, reference_time: i64, key_prefix: &[u8], mut f: F) {
self.for_each(reference_time, key_prefix, |k, _| f(k));
}
/// Iterate through keys and values beneath a key prefix, stopping at the end or if the function returns false.
fn for_each<F: FnMut(&[u8], &[u8]) -> bool>(&self, reference_time: i64, key_prefix: &[u8], f: F);
}
/// A simple in-memory data store backed by a BTreeMap.
pub struct MemoryDatabase<const KEY_SIZE: usize> {
max_age: i64,
db: Mutex<BTreeMap<[u8; KEY_SIZE], (i64, Arc<[u8]>)>>
}
impl<const KEY_SIZE: usize> MemoryDatabase<KEY_SIZE> {
pub fn new(max_age: i64) -> Self {
Self {
max_age: if max_age > 0 { max_age } else { i64::MAX },
db: Mutex::new(BTreeMap::new())
}
}
}
impl<const KEY_SIZE: usize> Database for MemoryDatabase<KEY_SIZE> {
type LoadResultValueType = Arc<[u8]>;
const KEY_SIZE: usize = KEY_SIZE;
const MAX_VALUE_SIZE: usize = 65536;
const KEY_IS_COMPUTED: bool = false;
fn load(&self, reference_time: i64, key: &[u8]) -> LoadResult<Self::LoadResultValueType> {
let db = self.db.lock().unwrap();
let e = db.get(key);
if e.is_some() {
let e = e.unwrap();
if (reference_time - e.0) <= self.max_age {
LoadResult::Ok(e.1.clone())
} else {
LoadResult::NotFound
}
} else {
LoadResult::NotFound
}
}
fn store(&self, key: &[u8], value: &[u8]) -> StoreResult {
let ts = crate::ms_since_epoch();
let mut isdup = false;
self.db.lock().unwrap().entry(key.try_into().unwrap()).and_modify(|e| {
if e.1.as_ref().eq(value) {
isdup = true;
} else {
*e = (ts, Arc::from(value));
}
}).or_insert_with(|| {
(ts, Arc::from(value))
});
if isdup {
StoreResult::Duplicate
} else {
StoreResult::Ok
}
}
fn for_each<F: FnMut(&[u8], &[u8]) -> bool>(&self, reference_time: i64, key_prefix: &[u8], mut f: F) {
let mut r_start = [0_u8; KEY_SIZE];
let mut r_end = [0xff_u8; KEY_SIZE];
(&mut r_start[0..key_prefix.len()]).copy_from_slice(key_prefix);
(&mut r_end[0..key_prefix.len()]).copy_from_slice(key_prefix);
for (k, v) in self.db.lock().unwrap().range((Included(r_start), Included(r_end))) {
if (reference_time - v.0) <= self.max_age {
if !f(k, &v.1) {
break;
}
}
}
}
}
impl<const KEY_SIZE: usize> PartialEq for MemoryDatabase<KEY_SIZE> {
fn eq(&self, other: &Self) -> bool {
self.max_age == other.max_age && self.db.lock().unwrap().eq(&*other.db.lock().unwrap())
}
}
impl<const KEY_SIZE: usize> Eq for MemoryDatabase<KEY_SIZE> {}

38
syncwhole/src/host.rs Normal file
View file

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::collections::HashSet;
use std::error::Error;
use std::net::SocketAddr;
/// A trait that users of syncwhole implement to provide configuration information and listen for events.
pub trait Host: Sync + Send {
/// Compute SHA512.
fn sha512(msg: &[u8]) -> [u8; 64];
/// Get a list of endpoints to which we always want to try to stay connected.
///
/// The node will repeatedly try to connect to these until a link is established and
/// reconnect on link failure. They should be stable well known nodes for this domain.
fn get_static_endpoints(&self) -> &[SocketAddr];
/// Get additional endpoints to try.
///
/// This should return any endpoints not in the supplied endpoint set if the size
/// of the set is less than the minimum active link count the host wishes to maintain.
fn get_more_endpoints(&self, current_endpoints: &HashSet<SocketAddr>) -> Vec<SocketAddr>;
/// Called whenever we have successfully connected to a remote endpoint to (possibly) remember it.
fn on_connect_success(&self, endpoint: &SocketAddr);
/// Called whenever an outgoing connection fails.
fn on_connect_failure(&self, endpoint: &SocketAddr, reason: Box<dyn Error>);
/// Fill a buffer with secure random bytes.
fn get_secure_random(&self, buf: &mut [u8]);
}

261
syncwhole/src/iblt.rs Normal file
View file

@ -0,0 +1,261 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::io::{Read, Write};
use std::mem::{size_of, zeroed};
use std::ptr::write_bytes;
use crate::varint;
// max value: 6, 5 was determined to be good via empirical testing
const KEY_MAPPING_ITERATIONS: usize = 5;
#[inline(always)]
fn xorshift64(mut x: u64) -> u64 {
x ^= x.wrapping_shl(13);
x ^= x.wrapping_shr(7);
x ^= x.wrapping_shl(17);
x
}
#[inline(always)]
fn splitmix64(mut x: u64) -> u64 {
x = u64::from_le(x);
x ^= x.wrapping_shr(30);
x = x.wrapping_mul(0xbf58476d1ce4e5b9);
x ^= x.wrapping_shr(27);
x = x.wrapping_mul(0x94d049bb133111eb);
x ^= x.wrapping_shr(31);
x.to_le()
}
#[inline(always)]
fn splitmix64_inverse(mut x: u64) -> u64 {
x = u64::from_le(x);
x ^= x.wrapping_shr(31) ^ x.wrapping_shr(62);
x = x.wrapping_mul(0x319642b2d24d8ec3);
x ^= x.wrapping_shr(27) ^ x.wrapping_shr(54);
x = x.wrapping_mul(0x96de1b173f119089);
x ^= x.wrapping_shr(30) ^ x.wrapping_shr(60);
x.to_le()
}
// https://nullprogram.com/blog/2018/07/31/
#[inline(always)]
fn triple32(mut x: u32) -> u32 {
x ^= x.wrapping_shr(17);
x = x.wrapping_mul(0xed5ad4bb);
x ^= x.wrapping_shr(11);
x = x.wrapping_mul(0xac4c1b51);
x ^= x.wrapping_shr(15);
x = x.wrapping_mul(0x31848bab);
x ^= x.wrapping_shr(14);
x
}
#[inline(always)]
fn next_iteration_index(prev_iteration_index: u64, salt: u64) -> u64 {
prev_iteration_index.wrapping_add(triple32(prev_iteration_index.wrapping_shr(32) as u32) as u64).wrapping_add(salt)
}
#[derive(Clone, PartialEq, Eq)]
struct IBLTEntry {
key_sum: u64,
check_hash_sum: u64,
count: i64
}
impl IBLTEntry {
#[inline(always)]
fn is_singular(&self) -> bool {
if self.count == 1 || self.count == -1 {
xorshift64(self.key_sum) == self.check_hash_sum
} else {
false
}
}
}
/// An Invertible Bloom Lookup Table for set reconciliation with 64-bit hashes.
#[derive(Clone, PartialEq, Eq)]
pub struct IBLT<const B: usize> {
salt: u64,
map: [IBLTEntry; B]
}
impl<const B: usize> IBLT<B> {
pub const BUCKETS: usize = B;
pub fn new(salt: u64) -> Self {
Self {
salt,
map: unsafe { zeroed() }
}
}
pub fn reset(&mut self, salt: u64) {
self.salt = salt;
unsafe { write_bytes((&mut self.map as *mut IBLTEntry).cast::<u8>(), 0, size_of::<[IBLTEntry; B]>()) };
}
pub fn read<R: Read>(&mut self, r: &mut R) -> std::io::Result<()> {
r.read_exact(unsafe { &mut *(&mut self.salt as *mut u64).cast::<[u8; 8]>() })?;
let mut prev_c = 0_i64;
for b in self.map.iter_mut() {
r.read_exact(unsafe { &mut *(&mut b.key_sum as *mut u64).cast::<[u8; 8]>() })?;
r.read_exact(unsafe { &mut *(&mut b.check_hash_sum as *mut u64).cast::<[u8; 8]>() })?;
let mut c = varint::read(r)? as i64;
if (c & 1) == 0 {
c = c.wrapping_shr(1);
} else {
c = -c.wrapping_shr(1);
}
b.count = c + prev_c;
prev_c = b.count;
}
Ok(())
}
pub fn write<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
w.write_all(unsafe { &*(&self.salt as *const u64).cast::<[u8; 8]>() })?;
let mut prev_c = 0_i64;
for b in self.map.iter() {
w.write_all(unsafe { &*(&b.key_sum as *const u64).cast::<[u8; 8]>() })?;
w.write_all(unsafe { &*(&b.check_hash_sum as *const u64).cast::<[u8; 8]>() })?;
let mut c = (b.count - prev_c).wrapping_shl(1);
prev_c = b.count;
if c < 0 {
c = -c | 1;
}
varint::write(w, c as u64)?;
}
Ok(())
}
fn ins_rem(&mut self, mut key: u64, delta: i64) {
key = splitmix64(key ^ self.salt);
let check_hash = xorshift64(key);
let mut iteration_index = u64::from_le(key);
for _ in 0..KEY_MAPPING_ITERATIONS {
iteration_index = next_iteration_index(iteration_index, self.salt);
let b = unsafe { self.map.get_unchecked_mut((iteration_index as usize) % B) };
b.key_sum ^= key;
b.check_hash_sum ^= check_hash;
b.count += delta;
}
}
/// Insert a 64-bit key.
/// Panics if the key is shorter than 64 bits. If longer, bits beyond 64 are ignored.
#[inline(always)]
pub fn insert(&mut self, key: &[u8]) {
assert!(key.len() >= 8);
self.ins_rem(unsafe { u64::from_ne_bytes(*(key.as_ptr().cast::<[u8; 8]>())) }, 1);
}
/// Remove a 64-bit key.
/// Panics if the key is shorter than 64 bits. If longer, bits beyond 64 are ignored.
#[inline(always)]
pub fn remove(&mut self, key: &[u8]) {
assert!(key.len() >= 8);
self.ins_rem(unsafe { u64::from_ne_bytes(*(key.as_ptr().cast::<[u8; 8]>())) }, -1);
}
pub fn subtract(&mut self, other: &Self) {
for b in 0..B {
let s = &mut self.map[b];
let o = &other.map[b];
s.key_sum ^= o.key_sum;
s.check_hash_sum ^= o.check_hash_sum;
s.count += o.count;
}
}
pub fn list<F: FnMut(&[u8; 8])>(mut self, mut f: F) -> bool {
let mut singular_buckets = [0_usize; B];
let mut singular_bucket_count = 0_usize;
for b in 0..B {
if self.map[b].is_singular() {
singular_buckets[singular_bucket_count] = b;
singular_bucket_count += 1;
}
}
while singular_bucket_count > 0 {
singular_bucket_count -= 1;
let b = &self.map[singular_buckets[singular_bucket_count]];
if b.is_singular() {
let key = b.key_sum;
let check_hash = xorshift64(key);
let mut iteration_index = u64::from_le(key);
f(&(splitmix64_inverse(key) ^ self.salt).to_ne_bytes());
for _ in 0..KEY_MAPPING_ITERATIONS {
iteration_index = next_iteration_index(iteration_index, self.salt);
let b_idx = (iteration_index as usize) % B;
let b = unsafe { self.map.get_unchecked_mut(b_idx) };
b.key_sum ^= key;
b.check_hash_sum ^= check_hash;
b.count -= 1;
if b.is_singular() {
if singular_bucket_count >= B {
// This would indicate an invalid IBLT.
return false;
}
singular_buckets[singular_bucket_count] = b_idx;
singular_bucket_count += 1;
}
}
}
}
return true;
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::time::SystemTime;
use crate::iblt::*;
#[test]
fn splitmix_is_invertiblex() {
for i in 1..1024_u64 {
assert_eq!(i, splitmix64_inverse(splitmix64(i)))
}
}
#[test]
fn insert_and_list() {
let mut rn = xorshift64(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos() as u64);
for _ in 0..256 {
let mut alice: IBLT<1024> = IBLT::new(rn);
rn = xorshift64(rn);
let mut expected: HashSet<u64> = HashSet::with_capacity(1024);
let count = 600;
for _ in 0..count {
let x = rn;
rn = xorshift64(rn);
expected.insert(x);
alice.insert(&x.to_ne_bytes());
}
let mut cnt = 0;
alice.list(|x| {
let x = u64::from_ne_bytes(*x);
cnt += 1;
assert!(expected.contains(&x));
});
assert_eq!(cnt, count);
}
}
}

14
syncwhole/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
pub(crate) mod varint;
pub(crate) mod protocol;
pub(crate) mod iblt;
pub mod database;
pub mod node;
pub mod host;
pub(crate) fn ms_since_epoch() -> i64 {
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64
}
pub(crate) fn ms_monotonic() -> i64 {
std::time::Instant::now().elapsed().as_millis() as i64
}

288
syncwhole/src/node.rs Normal file
View file

@ -0,0 +1,288 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::{Arc, Weak};
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::{TcpListener, TcpSocket, TcpStream};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use crate::database::Database;
use crate::host::Host;
use crate::ms_monotonic;
use crate::protocol::*;
use crate::varint;
const CONNECTION_TIMEOUT: i64 = 60000;
const CONNECTION_KEEPALIVE_AFTER: i64 = 20000;
struct Connection {
writer: Mutex<OwnedWriteHalf>,
last_send_time: AtomicI64,
last_receive_time: AtomicI64,
io_task: std::sync::Mutex<Option<JoinHandle<std::io::Result<()>>>>,
incoming: bool
}
impl Connection {
async fn send(&self, data: &[u8], now: i64) -> std::io::Result<()> {
self.writer.lock().await.write_all(data).await.map(|_| {
self.last_send_time.store(now, Ordering::Relaxed);
})
}
async fn send_obj<O: Serialize>(&self, message_type: u8, obj: &O, now: i64) -> std::io::Result<()> {
let data = rmp_serde::encode::to_vec_named(&obj);
if data.is_ok() {
let data = data.unwrap();
let mut tmp = [0_u8; 16];
tmp[0] = message_type;
let len = 1 + varint::encode(&mut tmp[1..], data.len() as u64);
let mut writer = self.writer.lock().await;
writer.write_all(&tmp[0..len]).await?;
writer.write_all(data.as_slice()).await?;
self.last_send_time.store(now, Ordering::Relaxed);
Ok(())
} else {
Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "serialize failure"))
}
}
fn kill(&self) {
let _ = self.io_task.lock().unwrap().take().map(|h| h.abort());
}
async fn read_msg<'a>(&self, reader: &mut BufReader<OwnedReadHalf>, buf: &'a mut Vec<u8>, message_size: usize, now: i64) -> std::io::Result<&'a [u8]> {
if message_size > buf.len() {
buf.resize(((message_size / 4096) + 1) * 4096, 0);
}
let b = &mut buf.as_mut_slice()[0..message_size];
reader.read_exact(b).await?;
self.last_receive_time.store(now, Ordering::Relaxed);
Ok(b)
}
async fn read_obj<'a, O: Deserialize<'a>>(&self, reader: &mut BufReader<OwnedReadHalf>, buf: &'a mut Vec<u8>, message_size: usize, now: i64) -> std::io::Result<O> {
rmp_serde::from_slice(self.read_msg(reader, buf, message_size, now).await?).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))
}
}
pub struct NodeInternal<D: Database + 'static, H: Host + 'static> {
anti_loopback_secret: [u8; 64],
db: Arc<D>,
host: Arc<H>,
bind_address: SocketAddr,
connections: Mutex<HashMap<SocketAddr, Weak<Connection>>>,
}
impl<D: Database + 'static, H: Host + 'static> NodeInternal<D, H> {
async fn housekeeping_task_main(self: Arc<Self>) {
loop {
tokio::time::sleep(Duration::from_millis(CONNECTION_KEEPALIVE_AFTER as u64)).await;
let mut to_ping: Vec<Arc<Connection>> = Vec::new();
let mut connections = self.connections.lock().await;
let now = ms_monotonic();
connections.retain(|_, c| {
c.upgrade().map_or(false, |c| {
if (now - c.last_receive_time.load(Ordering::Relaxed)) < CONNECTION_TIMEOUT {
if (now - c.last_send_time.load(Ordering::Relaxed)) > CONNECTION_KEEPALIVE_AFTER {
to_ping.push(c);
}
} else {
c.kill();
return false;
}
return true;
})
});
drop(connections); // release lock
for c in to_ping.iter() {
if c.send(&[MESSAGE_TYPE_NOP, 0], now).await.is_err() {
c.kill();
}
}
}
}
async fn listener_task_main(self: Arc<Self>, listener: TcpListener) {
loop {
let socket = listener.accept().await;
if socket.is_ok() {
let (stream, endpoint) = socket.unwrap();
Self::connection_start(&self, endpoint, stream, true).await;
}
}
}
async fn connection_io_task_main(self: Arc<Self>, connection: Arc<Connection>, reader: OwnedReadHalf) -> std::io::Result<()> {
let mut challenge = [0_u8; 16];
self.host.get_secure_random(&mut challenge);
connection.send_obj(MESSAGE_TYPE_INIT, &msg::Init {
anti_loopback_challenge: &challenge,
domain: String::new(), // TODO
key_size: D::KEY_SIZE as u16,
max_value_size: D::MAX_VALUE_SIZE as u64,
node_name: None,
node_contact: None,
preferred_ipv4: None,
preferred_ipv6: None
}, ms_monotonic()).await?;
let mut initialized = false;
let mut reader = BufReader::with_capacity(65536, reader);
let mut buf: Vec<u8> = Vec::new();
buf.resize(4096, 0);
loop {
reader.read_exact(&mut buf.as_mut_slice()[0..1]).await?;
let message_type = unsafe { *buf.get_unchecked(0) };
let message_size = varint::read_async(&mut reader).await?;
if message_size > (D::MAX_VALUE_SIZE + ((D::KEY_SIZE + 10) * 256) + 65536) as u64 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "message too large"));
}
let now = ms_monotonic();
match message_type {
MESSAGE_TYPE_INIT => {
if initialized {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "duplicate init"));
}
let msg: msg::Init = connection.read_obj(&mut reader, &mut buf, message_size as usize, now).await?;
let mut antiloop = msg.anti_loopback_challenge.to_vec();
let _ = std::io::Write::write_all(&mut antiloop, &self.anti_loopback_secret);
let antiloop = H::sha512(antiloop.as_slice());
connection.send_obj(MESSAGE_TYPE_INIT_RESPONSE, &msg::InitResponse {
anti_loopback_response: &antiloop[0..16]
}, now).await?;
initialized = true;
},
_ => {
// Skip messages that aren't recognized or don't need to be parsed like NOP.
let mut remaining = message_size as usize;
while remaining > 0 {
let s = remaining.min(buf.len());
reader.read_exact(&mut buf.as_mut_slice()[0..s]).await?;
remaining -= s;
}
connection.last_receive_time.store(ms_monotonic(), Ordering::Relaxed);
}
}
}
}
async fn connection_start(self: &Arc<Self>, endpoint: SocketAddr, stream: TcpStream, incoming: bool) -> bool {
let (reader, writer) = stream.into_split();
let mut ok = false;
let _ = self.connections.lock().await.entry(endpoint.clone()).or_insert_with(|| {
ok = true;
let now = ms_monotonic();
let connection = Arc::new(Connection {
writer: Mutex::new(writer),
last_send_time: AtomicI64::new(now),
last_receive_time: AtomicI64::new(now),
io_task: std::sync::Mutex::new(None),
incoming
});
let _ = connection.io_task.lock().unwrap().insert(tokio::spawn(Self::connection_io_task_main(self.clone(), connection.clone(), reader)));
Arc::downgrade(&connection)
});
ok
}
async fn connect(self: &Arc<Self>, endpoint: &SocketAddr) -> std::io::Result<bool> {
if !self.connections.lock().await.contains_key(endpoint) {
let stream = if endpoint.is_ipv4() { TcpSocket::new_v4() } else { TcpSocket::new_v6() }?;
if stream.set_reuseport(true).is_err() {
stream.set_reuseaddr(true)?;
}
stream.bind(self.bind_address.clone())?;
let stream = stream.connect(endpoint.clone()).await?;
Ok(self.connection_start(endpoint.clone(), stream, false).await)
} else {
Ok(false)
}
}
}
impl<D: Database + 'static, H: Host + 'static> Drop for NodeInternal<D, H> {
fn drop(&mut self) {
for (_, c) in self.connections.blocking_lock().drain() {
let _ = c.upgrade().map(|c| c.kill());
}
}
}
pub struct Node<D: Database + 'static, H: Host + 'static> {
internal: Arc<NodeInternal<D, H>>,
housekeeping_task: JoinHandle<()>,
listener_task: JoinHandle<()>
}
impl<D: Database + 'static, H: Host + 'static> Node<D, H> {
pub async fn new(db: Arc<D>, host: Arc<H>, bind_address: SocketAddr) -> std::io::Result<Self> {
let listener = if bind_address.is_ipv4() { TcpSocket::new_v4() } else { TcpSocket::new_v6() }?;
if listener.set_reuseport(true).is_err() {
listener.set_reuseaddr(true)?;
}
listener.bind(bind_address.clone())?;
let listener = listener.listen(1024)?;
let internal = Arc::new(NodeInternal::<D, H> {
anti_loopback_secret: {
let mut tmp = [0_u8; 64];
host.get_secure_random(&mut tmp);
tmp
},
db: db.clone(),
host: host.clone(),
bind_address,
connections: Mutex::new(HashMap::with_capacity(64)),
});
Ok(Self {
internal: internal.clone(),
housekeeping_task: tokio::spawn(internal.clone().housekeeping_task_main()),
listener_task: tokio::spawn(internal.listener_task_main(listener)),
})
}
#[inline(always)]
pub async fn connect(&self, endpoint: &SocketAddr) -> std::io::Result<bool> {
self.internal.connect(endpoint).await
}
pub fn list_connections(&self) -> Vec<SocketAddr> {
let mut connections = self.internal.connections.blocking_lock();
let mut cl: Vec<SocketAddr> = Vec::with_capacity(connections.len());
connections.retain(|sa, c| {
if c.strong_count() > 0 {
cl.push(sa.clone());
true
} else {
false
}
});
cl
}
}
impl<D: Database + 'static, H: Host + 'static> Drop for Node<D, H> {
fn drop(&mut self) {
self.housekeeping_task.abort();
self.listener_task.abort();
}
}

59
syncwhole/src/protocol.rs Normal file
View file

@ -0,0 +1,59 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
pub const MESSAGE_TYPE_NOP: u8 = 0;
pub const MESSAGE_TYPE_INIT: u8 = 1;
pub const MESSAGE_TYPE_INIT_RESPONSE: u8 = 2;
pub const MESSAGE_TYPE_HAVE_RECORDS: u8 = 3;
pub const MESSAGE_TYPE_GET_RECORDS: u8 = 4;
pub mod msg {
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct IPv4 {
#[serde(rename = "i")]
pub ip: [u8; 4],
#[serde(rename = "p")]
pub port: u16
}
#[derive(Serialize, Deserialize)]
pub struct IPv6 {
#[serde(rename = "i")]
pub ip: [u8; 16],
#[serde(rename = "p")]
pub port: u16
}
#[derive(Serialize, Deserialize)]
pub struct Init<'a> {
#[serde(rename = "alc")]
pub anti_loopback_challenge: &'a [u8],
#[serde(rename = "d")]
pub domain: String,
#[serde(rename = "ks")]
pub key_size: u16,
#[serde(rename = "mvs")]
pub max_value_size: u64,
#[serde(rename = "nn")]
pub node_name: Option<String>,
#[serde(rename = "nc")]
pub node_contact: Option<String>,
#[serde(rename = "pi4")]
pub preferred_ipv4: Option<IPv4>,
#[serde(rename = "pi6")]
pub preferred_ipv6: Option<IPv6>,
}
#[derive(Serialize, Deserialize)]
pub struct InitResponse<'a> {
#[serde(rename = "alr")]
pub anti_loopback_response: &'a [u8],
}
}

76
syncwhole/src/varint.rs Normal file
View file

@ -0,0 +1,76 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2022 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::io::{Read, Write};
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
const VARINT_MAX_SIZE_BYTES: usize = 10;
pub fn encode(b: &mut [u8], mut v: u64) -> usize {
let mut i = 0;
loop {
if v > 0x7f {
b[i] = (v as u8) & 0x7f;
i += 1;
v = v.wrapping_shr(7);
} else {
b[i] = (v as u8) | 0x80;
i += 1;
break;
}
}
i
}
#[inline(always)]
pub async fn write_async<W: AsyncWrite + Unpin>(w: &mut W, v: u64) -> std::io::Result<()> {
let mut b = [0_u8; VARINT_MAX_SIZE_BYTES];
let i = encode(&mut b, v);
w.write_all(&b[0..i]).await
}
pub async fn read_async<R: AsyncRead + Unpin>(r: &mut R) -> std::io::Result<u64> {
let mut v = 0_u64;
let mut buf = [0_u8; 1];
let mut pos = 0;
loop {
let _ = r.read_exact(&mut buf).await?;
let b = buf[0];
if b <= 0x7f {
v |= (b as u64).wrapping_shl(pos);
pos += 7;
} else {
v |= ((b & 0x7f) as u64).wrapping_shl(pos);
return Ok(v);
}
}
}
#[inline(always)]
pub fn write<W: Write>(w: &mut W, v: u64) -> std::io::Result<()> {
let mut b = [0_u8; VARINT_MAX_SIZE_BYTES];
let i = encode(&mut b, v);
w.write_all(&b[0..i])
}
pub fn read<R: Read>(r: &mut R) -> std::io::Result<u64> {
let mut v = 0_u64;
let mut buf = [0_u8; 1];
let mut pos = 0;
loop {
let _ = r.read_exact(&mut buf)?;
let b = buf[0];
if b <= 0x7f {
v |= (b as u64).wrapping_shl(pos);
pos += 7;
} else {
v |= ((b & 0x7f) as u64).wrapping_shl(pos);
return Ok(v);
}
}
}

View file

@ -137,14 +137,15 @@ impl Identity {
fingerprint: [0_u8; 64] // replaced in upgrade()
};
assert!(id.upgrade().is_ok());
assert!(id.p384.is_some() && id.secret.as_ref().unwrap().p384.is_some());
id
}
/// Upgrade older x25519-only identities to hybrid identities with both x25519 and NIST P-384 curves.
///
/// The identity must contain its x25519 secret key or an error occurs. If the identity is already
/// a new form hybrid identity nothing happens and Ok is returned.
pub fn upgrade(&mut self) -> Result<(), InvalidParameterError> {
/// The boolean indicates whether or not an upgrade occurred. An error occurs if this identity is
/// invalid or missing its private key(s). This does nothing if no upgrades are possible.
pub fn upgrade(&mut self) -> Result<bool, InvalidParameterError> {
if self.secret.is_none() {
return Err(InvalidParameterError("an identity can only be upgraded if it includes its private key"));
}
@ -179,10 +180,11 @@ impl Identity {
ecdh: p384_ecdh,
ecdsa: p384_ecdsa,
});
self.fingerprint = SHA512::hash(self_sign_buf.as_slice());
return Ok(true);
}
return Ok(());
return Ok(false);
}
#[inline(always)]

View file

@ -35,10 +35,6 @@ pub trait SystemInterface: Sync + Send {
/// Node is shutting down.
fn event_node_is_down(&self);
/// A root signaled an identity collision.
/// This should cause the external code to shut down this node, delete its identity, and recreate.
fn event_identity_collision(&self);
/// Node has gone online or offline.
fn event_online_status_change(&self, online: bool);
@ -49,8 +45,7 @@ pub trait SystemInterface: Sync + Send {
fn load_node_identity(&self) -> Option<Vec<u8>>;
/// Save this node's identity.
/// Note that this is only called on first startup (after up) and after identity_changed.
fn save_node_identity(&self, id: &Identity, public: &[u8], secret: &[u8]);
fn save_node_identity(&self, id: &Identity);
/// Called to send a packet over the physical network (virtual -> physical).
///
@ -145,14 +140,14 @@ pub struct Node {
impl Node {
/// Create a new Node.
pub fn new<SI: SystemInterface>(si: &SI, auto_generate_identity: bool) -> Result<Self, InvalidParameterError> {
let id = {
let mut id = {
let id_str = si.load_node_identity();
if id_str.is_none() {
if !auto_generate_identity {
return Err(InvalidParameterError("no identity found and auto-generate not enabled"));
} else {
let id = Identity::generate();
si.save_node_identity(&id, id.to_string().as_bytes(), id.to_secret_string().as_bytes());
si.save_node_identity(&id);
id
}
} else {
@ -166,6 +161,11 @@ impl Node {
}
};
// Automatically upgrade old type identities to add P-384 keys.
if id.upgrade()? {
si.save_node_identity(&id);
}
Ok(Self {
instance_id: zerotier_core_crypto::random::next_u64_secure(),
identity: id,
@ -203,6 +203,14 @@ impl Node {
let mut intervals = self.intervals.lock();
let tt = si.time_ticks();
if intervals.peers.gate(tt) {
self.peers.retain(|_, peer| {
peer.call_every_interval(si, tt);
todo!();
true
});
}
if intervals.paths.gate(tt) {
self.paths.retain(|_, path| {
path.upgrade().map_or(false, |p| {
@ -212,14 +220,6 @@ impl Node {
});
}
if intervals.peers.gate(tt) {
self.peers.retain(|_, peer| {
peer.call_every_interval(si, tt);
todo!();
true
});
}
if intervals.whois.gate(tt) {
self.whois.call_every_interval(self, si, tt);
}