mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-12 19:25:18 +00:00
detangling merge of main node for v2 (#293)
This commit is contained in:
parent
ab065006b4
commit
b4051ccbc9
596
Cargo.lock
generated
596
Cargo.lock
generated
@ -2,6 +2,41 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -130,6 +165,24 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "basic-toml"
|
||||
version = "0.1.9"
|
||||
@ -160,6 +213,18 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
@ -169,7 +234,16 @@ dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array",
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -260,6 +334,26 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "channel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64",
|
||||
"criterion 0.4.0",
|
||||
"ed448-goldilocks-plus",
|
||||
"hex 0.4.3",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"lazy_static",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"thiserror",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.2"
|
||||
@ -287,6 +381,16 @@ dependencies = [
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.25"
|
||||
@ -363,6 +467,21 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.4.0"
|
||||
@ -456,13 +575,108 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed448-goldilocks-plus"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d1a0afcce0f2d28faeda0c9fea990ea5832b9a13a4ecff26d2486fcecc5c014"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
"hex 0.4.3",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"sha3",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -471,12 +685,53 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"base64ct",
|
||||
"crypto-bigint",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"generic-array 0.14.7",
|
||||
"group",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"sec1",
|
||||
"serde_json",
|
||||
"serdect",
|
||||
"subtle",
|
||||
"tap",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.11.0"
|
||||
@ -486,6 +741,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
@ -495,6 +756,17 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
@ -506,6 +778,16 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||
dependencies = [
|
||||
"opaque-debug 0.3.1",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
@ -523,6 +805,17 @@ dependencies = [
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.4.1"
|
||||
@ -578,6 +871,24 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@ -588,6 +899,15 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array 0.14.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.12"
|
||||
@ -629,6 +949,15 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@ -685,6 +1014,70 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@ -718,6 +1111,12 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.6.1"
|
||||
@ -730,6 +1129,25 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
@ -764,6 +1182,18 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug 0.3.1",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@ -788,6 +1218,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@ -867,6 +1303,31 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "rpm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion 0.4.0",
|
||||
"curve25519-dalek",
|
||||
"hex 0.4.3",
|
||||
"lazy_static",
|
||||
"num",
|
||||
"rand",
|
||||
"rayon",
|
||||
"serde_json",
|
||||
"subtle",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
@ -902,6 +1363,21 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array 0.14.7",
|
||||
"pkcs8",
|
||||
"serdect",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
@ -913,18 +1389,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
version = "1.0.208"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -942,16 +1418,47 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serdect"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -960,6 +1467,16 @@ version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@ -972,6 +1489,12 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.66"
|
||||
@ -983,6 +1506,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.16.1"
|
||||
@ -991,18 +1520,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1181,6 +1710,16 @@ dependencies = [
|
||||
"weedle2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
@ -1196,7 +1735,7 @@ dependencies = [
|
||||
"criterion 0.5.1",
|
||||
"hex 0.3.2",
|
||||
"num-traits",
|
||||
"sha2",
|
||||
"sha2 0.8.2",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
@ -1398,3 +1937,32 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
@ -14,8 +14,10 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/vdf",
|
||||
"crates/channel",
|
||||
"crates/classgroup",
|
||||
"crates/bls48581",
|
||||
"crates/rpm",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
9
channel/README.md
Normal file
9
channel/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Channel
|
||||
|
||||
Wrapper for the Rust implementation of Channel in [crates/channel](../crates/channel).
|
||||
|
||||
## Generate Go bindings
|
||||
|
||||
```sh
|
||||
go generate
|
||||
```
|
88
channel/channel.go
Normal file
88
channel/channel.go
Normal file
@ -0,0 +1,88 @@
|
||||
package channel
|
||||
|
||||
import (
|
||||
generated "source.quilibrium.com/quilibrium/monorepo/channel/generated/channel"
|
||||
)
|
||||
|
||||
//go:generate ./generate.sh
|
||||
|
||||
func NewDoubleRatchet(
|
||||
sessionKey []uint8,
|
||||
sendingHeaderKey []uint8,
|
||||
nextReceivingHeaderKey []uint8,
|
||||
isSender bool,
|
||||
sendingEphemeralPrivateKey []uint8,
|
||||
receivingEphemeralKey []uint8,
|
||||
) string {
|
||||
return generated.NewDoubleRatchet(
|
||||
sessionKey,
|
||||
sendingHeaderKey,
|
||||
nextReceivingHeaderKey,
|
||||
isSender,
|
||||
sendingEphemeralPrivateKey,
|
||||
receivingEphemeralKey,
|
||||
)
|
||||
}
|
||||
|
||||
func NewTripleRatchet(
|
||||
peers [][]uint8,
|
||||
peerKey []uint8,
|
||||
identityKey []uint8,
|
||||
signedPreKey []uint8,
|
||||
threshold uint64,
|
||||
asyncDkgRatchet bool,
|
||||
) generated.TripleRatchetStateAndMetadata {
|
||||
return generated.NewTripleRatchet(
|
||||
peers,
|
||||
peerKey,
|
||||
identityKey,
|
||||
signedPreKey,
|
||||
threshold,
|
||||
asyncDkgRatchet,
|
||||
)
|
||||
}
|
||||
|
||||
func DoubleRatchetEncrypt(
|
||||
ratchetStateAndMessage generated.DoubleRatchetStateAndMessage,
|
||||
) generated.DoubleRatchetStateAndEnvelope {
|
||||
return generated.DoubleRatchetEncrypt(ratchetStateAndMessage)
|
||||
}
|
||||
|
||||
func DoubleRatchetDecrypt(
|
||||
ratchetStateAndEnvelope generated.DoubleRatchetStateAndEnvelope,
|
||||
) generated.DoubleRatchetStateAndMessage {
|
||||
return generated.DoubleRatchetDecrypt(ratchetStateAndEnvelope)
|
||||
}
|
||||
|
||||
func TripleRatchetInitRound1(
|
||||
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
|
||||
) generated.TripleRatchetStateAndMetadata {
|
||||
return generated.TripleRatchetInitRound1(ratchetStateAndMetadata)
|
||||
}
|
||||
func TripleRatchetInitRound2(
|
||||
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
|
||||
) generated.TripleRatchetStateAndMetadata {
|
||||
return generated.TripleRatchetInitRound2(ratchetStateAndMetadata)
|
||||
}
|
||||
func TripleRatchetInitRound3(
|
||||
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
|
||||
) generated.TripleRatchetStateAndMetadata {
|
||||
return generated.TripleRatchetInitRound3(ratchetStateAndMetadata)
|
||||
}
|
||||
func TripleRatchetInitRound4(
|
||||
ratchetStateAndMetadata generated.TripleRatchetStateAndMetadata,
|
||||
) generated.TripleRatchetStateAndMetadata {
|
||||
return generated.TripleRatchetInitRound4(ratchetStateAndMetadata)
|
||||
}
|
||||
|
||||
func TripleRatchetEncrypt(
|
||||
ratchetStateAndMessage generated.TripleRatchetStateAndMessage,
|
||||
) generated.TripleRatchetStateAndEnvelope {
|
||||
return generated.TripleRatchetEncrypt(ratchetStateAndMessage)
|
||||
}
|
||||
|
||||
func TripleRatchetDecrypt(
|
||||
ratchetStateAndEnvelope generated.TripleRatchetStateAndEnvelope,
|
||||
) generated.TripleRatchetStateAndMessage {
|
||||
return generated.TripleRatchetDecrypt(ratchetStateAndEnvelope)
|
||||
}
|
14
channel/generate.sh
Executable file
14
channel/generate.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -euxo pipefail
|
||||
|
||||
ROOT_DIR="${ROOT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
|
||||
|
||||
RUST_CHANNEL_PACKAGE="$ROOT_DIR/crates/channel"
|
||||
BINDINGS_DIR="$ROOT_DIR/channel"
|
||||
|
||||
# Build the Rust Channel package in release mode
|
||||
cargo build -p channel --release
|
||||
|
||||
# Generate Go bindings
|
||||
pushd "$RUST_CHANNEL_PACKAGE" > /dev/null
|
||||
uniffi-bindgen-go src/lib.udl -o "$BINDINGS_DIR"/generated
|
8
channel/generated/channel/channel.c
Normal file
8
channel/generated/channel/channel.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <channel.h>
|
||||
|
||||
// This file exists beacause of
|
||||
// https://github.com/golang/go/issues/11263
|
||||
|
||||
void cgo_rust_task_callback_bridge_channel(RustTaskCallback cb, const void * taskData, int8_t status) {
|
||||
cb(taskData, status);
|
||||
}
|
954
channel/generated/channel/channel.go
Normal file
954
channel/generated/channel/channel.go
Normal file
@ -0,0 +1,954 @@
|
||||
package channel
|
||||
|
||||
// #include <channel.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type RustBuffer = C.RustBuffer
|
||||
|
||||
type RustBufferI interface {
|
||||
AsReader() *bytes.Reader
|
||||
Free()
|
||||
ToGoBytes() []byte
|
||||
Data() unsafe.Pointer
|
||||
Len() int
|
||||
Capacity() int
|
||||
}
|
||||
|
||||
func RustBufferFromExternal(b RustBufferI) RustBuffer {
|
||||
return RustBuffer{
|
||||
capacity: C.int(b.Capacity()),
|
||||
len: C.int(b.Len()),
|
||||
data: (*C.uchar)(b.Data()),
|
||||
}
|
||||
}
|
||||
|
||||
func (cb RustBuffer) Capacity() int {
|
||||
return int(cb.capacity)
|
||||
}
|
||||
|
||||
func (cb RustBuffer) Len() int {
|
||||
return int(cb.len)
|
||||
}
|
||||
|
||||
func (cb RustBuffer) Data() unsafe.Pointer {
|
||||
return unsafe.Pointer(cb.data)
|
||||
}
|
||||
|
||||
func (cb RustBuffer) AsReader() *bytes.Reader {
|
||||
b := unsafe.Slice((*byte)(cb.data), C.int(cb.len))
|
||||
return bytes.NewReader(b)
|
||||
}
|
||||
|
||||
func (cb RustBuffer) Free() {
|
||||
rustCall(func(status *C.RustCallStatus) bool {
|
||||
C.ffi_channel_rustbuffer_free(cb, status)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (cb RustBuffer) ToGoBytes() []byte {
|
||||
return C.GoBytes(unsafe.Pointer(cb.data), C.int(cb.len))
|
||||
}
|
||||
|
||||
func stringToRustBuffer(str string) RustBuffer {
|
||||
return bytesToRustBuffer([]byte(str))
|
||||
}
|
||||
|
||||
func bytesToRustBuffer(b []byte) RustBuffer {
|
||||
if len(b) == 0 {
|
||||
return RustBuffer{}
|
||||
}
|
||||
// We can pass the pointer along here, as it is pinned
|
||||
// for the duration of this call
|
||||
foreign := C.ForeignBytes{
|
||||
len: C.int(len(b)),
|
||||
data: (*C.uchar)(unsafe.Pointer(&b[0])),
|
||||
}
|
||||
|
||||
return rustCall(func(status *C.RustCallStatus) RustBuffer {
|
||||
return C.ffi_channel_rustbuffer_from_bytes(foreign, status)
|
||||
})
|
||||
}
|
||||
|
||||
type BufLifter[GoType any] interface {
|
||||
Lift(value RustBufferI) GoType
|
||||
}
|
||||
|
||||
type BufLowerer[GoType any] interface {
|
||||
Lower(value GoType) RustBuffer
|
||||
}
|
||||
|
||||
type FfiConverter[GoType any, FfiType any] interface {
|
||||
Lift(value FfiType) GoType
|
||||
Lower(value GoType) FfiType
|
||||
}
|
||||
|
||||
type BufReader[GoType any] interface {
|
||||
Read(reader io.Reader) GoType
|
||||
}
|
||||
|
||||
type BufWriter[GoType any] interface {
|
||||
Write(writer io.Writer, value GoType)
|
||||
}
|
||||
|
||||
type FfiRustBufConverter[GoType any, FfiType any] interface {
|
||||
FfiConverter[GoType, FfiType]
|
||||
BufReader[GoType]
|
||||
}
|
||||
|
||||
func LowerIntoRustBuffer[GoType any](bufWriter BufWriter[GoType], value GoType) RustBuffer {
|
||||
// This might be not the most efficient way but it does not require knowing allocation size
|
||||
// beforehand
|
||||
var buffer bytes.Buffer
|
||||
bufWriter.Write(&buffer, value)
|
||||
|
||||
bytes, err := io.ReadAll(&buffer)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("reading written data: %w", err))
|
||||
}
|
||||
return bytesToRustBuffer(bytes)
|
||||
}
|
||||
|
||||
func LiftFromRustBuffer[GoType any](bufReader BufReader[GoType], rbuf RustBufferI) GoType {
|
||||
defer rbuf.Free()
|
||||
reader := rbuf.AsReader()
|
||||
item := bufReader.Read(reader)
|
||||
if reader.Len() > 0 {
|
||||
// TODO: Remove this
|
||||
leftover, _ := io.ReadAll(reader)
|
||||
panic(fmt.Errorf("Junk remaining in buffer after lifting: %s", string(leftover)))
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func rustCallWithError[U any](converter BufLifter[error], callback func(*C.RustCallStatus) U) (U, error) {
|
||||
var status C.RustCallStatus
|
||||
returnValue := callback(&status)
|
||||
err := checkCallStatus(converter, status)
|
||||
|
||||
return returnValue, err
|
||||
}
|
||||
|
||||
func checkCallStatus(converter BufLifter[error], status C.RustCallStatus) error {
|
||||
switch status.code {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return converter.Lift(status.errorBuf)
|
||||
case 2:
|
||||
// when the rust code sees a panic, it tries to construct a rustbuffer
|
||||
// with the message. but if that code panics, then it just sends back
|
||||
// an empty buffer.
|
||||
if status.errorBuf.len > 0 {
|
||||
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
|
||||
} else {
|
||||
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown status code: %d", status.code)
|
||||
}
|
||||
}
|
||||
|
||||
func checkCallStatusUnknown(status C.RustCallStatus) error {
|
||||
switch status.code {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
panic(fmt.Errorf("function not returning an error returned an error"))
|
||||
case 2:
|
||||
// when the rust code sees a panic, it tries to construct a rustbuffer
|
||||
// with the message. but if that code panics, then it just sends back
|
||||
// an empty buffer.
|
||||
if status.errorBuf.len > 0 {
|
||||
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
|
||||
} else {
|
||||
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown status code: %d", status.code)
|
||||
}
|
||||
}
|
||||
|
||||
func rustCall[U any](callback func(*C.RustCallStatus) U) U {
|
||||
returnValue, err := rustCallWithError(nil, callback)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
func writeInt8(writer io.Writer, value int8) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeUint8(writer io.Writer, value uint8) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeInt16(writer io.Writer, value int16) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeUint16(writer io.Writer, value uint16) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeInt32(writer io.Writer, value int32) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeUint32(writer io.Writer, value uint32) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeInt64(writer io.Writer, value int64) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeUint64(writer io.Writer, value uint64) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeFloat32(writer io.Writer, value float32) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeFloat64(writer io.Writer, value float64) {
|
||||
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func readInt8(reader io.Reader) int8 {
|
||||
var result int8
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readUint8(reader io.Reader) uint8 {
|
||||
var result uint8
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readInt16(reader io.Reader) int16 {
|
||||
var result int16
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readUint16(reader io.Reader) uint16 {
|
||||
var result uint16
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readInt32(reader io.Reader) int32 {
|
||||
var result int32
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readUint32(reader io.Reader) uint32 {
|
||||
var result uint32
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readInt64(reader io.Reader) int64 {
|
||||
var result int64
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readUint64(reader io.Reader) uint64 {
|
||||
var result uint64
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readFloat32(reader io.Reader) float32 {
|
||||
var result float32
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func readFloat64(reader io.Reader) float64 {
|
||||
var result float64
|
||||
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
uniffiCheckChecksums()
|
||||
}
|
||||
|
||||
func uniffiCheckChecksums() {
|
||||
// Get the bindings contract version from our ComponentInterface
|
||||
bindingsContractVersion := 24
|
||||
// Get the scaffolding contract version by calling the into the dylib
|
||||
scaffoldingContractVersion := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint32_t {
|
||||
return C.ffi_channel_uniffi_contract_version(uniffiStatus)
|
||||
})
|
||||
if bindingsContractVersion != int(scaffoldingContractVersion) {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: UniFFI contract version mismatch")
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_double_ratchet_decrypt(uniffiStatus)
|
||||
})
|
||||
if checksum != 57128 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_double_ratchet_decrypt: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_double_ratchet_encrypt(uniffiStatus)
|
||||
})
|
||||
if checksum != 10167 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_double_ratchet_encrypt: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_new_double_ratchet(uniffiStatus)
|
||||
})
|
||||
if checksum != 21249 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_new_double_ratchet: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_new_triple_ratchet(uniffiStatus)
|
||||
})
|
||||
if checksum != 11118 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_new_triple_ratchet: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_decrypt(uniffiStatus)
|
||||
})
|
||||
if checksum != 56417 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_decrypt: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_encrypt(uniffiStatus)
|
||||
})
|
||||
if checksum != 63768 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_encrypt: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_1(uniffiStatus)
|
||||
})
|
||||
if checksum != 48593 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_1: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_2(uniffiStatus)
|
||||
})
|
||||
if checksum != 55359 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_2: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_3(uniffiStatus)
|
||||
})
|
||||
if checksum != 50330 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_3: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
{
|
||||
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
|
||||
return C.uniffi_channel_checksum_func_triple_ratchet_init_round_4(uniffiStatus)
|
||||
})
|
||||
if checksum != 58513 {
|
||||
// If this happens try cleaning and rebuilding your project
|
||||
panic("channel: uniffi_channel_checksum_func_triple_ratchet_init_round_4: UniFFI API checksum mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type FfiConverterUint8 struct{}
|
||||
|
||||
var FfiConverterUint8INSTANCE = FfiConverterUint8{}
|
||||
|
||||
func (FfiConverterUint8) Lower(value uint8) C.uint8_t {
|
||||
return C.uint8_t(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint8) Write(writer io.Writer, value uint8) {
|
||||
writeUint8(writer, value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint8) Lift(value C.uint8_t) uint8 {
|
||||
return uint8(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint8) Read(reader io.Reader) uint8 {
|
||||
return readUint8(reader)
|
||||
}
|
||||
|
||||
type FfiDestroyerUint8 struct{}
|
||||
|
||||
func (FfiDestroyerUint8) Destroy(_ uint8) {}
|
||||
|
||||
type FfiConverterUint64 struct{}
|
||||
|
||||
var FfiConverterUint64INSTANCE = FfiConverterUint64{}
|
||||
|
||||
func (FfiConverterUint64) Lower(value uint64) C.uint64_t {
|
||||
return C.uint64_t(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Write(writer io.Writer, value uint64) {
|
||||
writeUint64(writer, value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Lift(value C.uint64_t) uint64 {
|
||||
return uint64(value)
|
||||
}
|
||||
|
||||
func (FfiConverterUint64) Read(reader io.Reader) uint64 {
|
||||
return readUint64(reader)
|
||||
}
|
||||
|
||||
type FfiDestroyerUint64 struct{}
|
||||
|
||||
func (FfiDestroyerUint64) Destroy(_ uint64) {}
|
||||
|
||||
type FfiConverterBool struct{}
|
||||
|
||||
var FfiConverterBoolINSTANCE = FfiConverterBool{}
|
||||
|
||||
func (FfiConverterBool) Lower(value bool) C.int8_t {
|
||||
if value {
|
||||
return C.int8_t(1)
|
||||
}
|
||||
return C.int8_t(0)
|
||||
}
|
||||
|
||||
func (FfiConverterBool) Write(writer io.Writer, value bool) {
|
||||
if value {
|
||||
writeInt8(writer, 1)
|
||||
} else {
|
||||
writeInt8(writer, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func (FfiConverterBool) Lift(value C.int8_t) bool {
|
||||
return value != 0
|
||||
}
|
||||
|
||||
func (FfiConverterBool) Read(reader io.Reader) bool {
|
||||
return readInt8(reader) != 0
|
||||
}
|
||||
|
||||
type FfiDestroyerBool struct{}
|
||||
|
||||
func (FfiDestroyerBool) Destroy(_ bool) {}
|
||||
|
||||
type FfiConverterString struct{}
|
||||
|
||||
var FfiConverterStringINSTANCE = FfiConverterString{}
|
||||
|
||||
func (FfiConverterString) Lift(rb RustBufferI) string {
|
||||
defer rb.Free()
|
||||
reader := rb.AsReader()
|
||||
b, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("reading reader: %w", err))
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (FfiConverterString) Read(reader io.Reader) string {
|
||||
length := readInt32(reader)
|
||||
buffer := make([]byte, length)
|
||||
read_length, err := reader.Read(buffer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if read_length != int(length) {
|
||||
panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length))
|
||||
}
|
||||
return string(buffer)
|
||||
}
|
||||
|
||||
func (FfiConverterString) Lower(value string) RustBuffer {
|
||||
return stringToRustBuffer(value)
|
||||
}
|
||||
|
||||
func (FfiConverterString) Write(writer io.Writer, value string) {
|
||||
if len(value) > math.MaxInt32 {
|
||||
panic("String is too large to fit into Int32")
|
||||
}
|
||||
|
||||
writeInt32(writer, int32(len(value)))
|
||||
write_length, err := io.WriteString(writer, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if write_length != len(value) {
|
||||
panic(fmt.Errorf("bad write length when writing string, expected %d, written %d", len(value), write_length))
|
||||
}
|
||||
}
|
||||
|
||||
type FfiDestroyerString struct{}
|
||||
|
||||
func (FfiDestroyerString) Destroy(_ string) {}
|
||||
|
||||
type DoubleRatchetStateAndEnvelope struct {
|
||||
RatchetState string
|
||||
Envelope string
|
||||
}
|
||||
|
||||
func (r *DoubleRatchetStateAndEnvelope) Destroy() {
|
||||
FfiDestroyerString{}.Destroy(r.RatchetState)
|
||||
FfiDestroyerString{}.Destroy(r.Envelope)
|
||||
}
|
||||
|
||||
type FfiConverterTypeDoubleRatchetStateAndEnvelope struct{}
|
||||
|
||||
var FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE = FfiConverterTypeDoubleRatchetStateAndEnvelope{}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Lift(rb RustBufferI) DoubleRatchetStateAndEnvelope {
|
||||
return LiftFromRustBuffer[DoubleRatchetStateAndEnvelope](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Read(reader io.Reader) DoubleRatchetStateAndEnvelope {
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Lower(value DoubleRatchetStateAndEnvelope) RustBuffer {
|
||||
return LowerIntoRustBuffer[DoubleRatchetStateAndEnvelope](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndEnvelope) Write(writer io.Writer, value DoubleRatchetStateAndEnvelope) {
|
||||
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
|
||||
FfiConverterStringINSTANCE.Write(writer, value.Envelope)
|
||||
}
|
||||
|
||||
type FfiDestroyerTypeDoubleRatchetStateAndEnvelope struct{}
|
||||
|
||||
func (_ FfiDestroyerTypeDoubleRatchetStateAndEnvelope) Destroy(value DoubleRatchetStateAndEnvelope) {
|
||||
value.Destroy()
|
||||
}
|
||||
|
||||
type DoubleRatchetStateAndMessage struct {
|
||||
RatchetState string
|
||||
Message []uint8
|
||||
}
|
||||
|
||||
func (r *DoubleRatchetStateAndMessage) Destroy() {
|
||||
FfiDestroyerString{}.Destroy(r.RatchetState)
|
||||
FfiDestroyerSequenceUint8{}.Destroy(r.Message)
|
||||
}
|
||||
|
||||
type FfiConverterTypeDoubleRatchetStateAndMessage struct{}
|
||||
|
||||
var FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE = FfiConverterTypeDoubleRatchetStateAndMessage{}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Lift(rb RustBufferI) DoubleRatchetStateAndMessage {
|
||||
return LiftFromRustBuffer[DoubleRatchetStateAndMessage](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Read(reader io.Reader) DoubleRatchetStateAndMessage {
|
||||
return DoubleRatchetStateAndMessage{
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
FfiConverterSequenceUint8INSTANCE.Read(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Lower(value DoubleRatchetStateAndMessage) RustBuffer {
|
||||
return LowerIntoRustBuffer[DoubleRatchetStateAndMessage](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeDoubleRatchetStateAndMessage) Write(writer io.Writer, value DoubleRatchetStateAndMessage) {
|
||||
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
|
||||
FfiConverterSequenceUint8INSTANCE.Write(writer, value.Message)
|
||||
}
|
||||
|
||||
type FfiDestroyerTypeDoubleRatchetStateAndMessage struct{}
|
||||
|
||||
func (_ FfiDestroyerTypeDoubleRatchetStateAndMessage) Destroy(value DoubleRatchetStateAndMessage) {
|
||||
value.Destroy()
|
||||
}
|
||||
|
||||
type TripleRatchetStateAndEnvelope struct {
|
||||
RatchetState string
|
||||
Envelope string
|
||||
}
|
||||
|
||||
func (r *TripleRatchetStateAndEnvelope) Destroy() {
|
||||
FfiDestroyerString{}.Destroy(r.RatchetState)
|
||||
FfiDestroyerString{}.Destroy(r.Envelope)
|
||||
}
|
||||
|
||||
type FfiConverterTypeTripleRatchetStateAndEnvelope struct{}
|
||||
|
||||
var FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE = FfiConverterTypeTripleRatchetStateAndEnvelope{}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Lift(rb RustBufferI) TripleRatchetStateAndEnvelope {
|
||||
return LiftFromRustBuffer[TripleRatchetStateAndEnvelope](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Read(reader io.Reader) TripleRatchetStateAndEnvelope {
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Lower(value TripleRatchetStateAndEnvelope) RustBuffer {
|
||||
return LowerIntoRustBuffer[TripleRatchetStateAndEnvelope](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndEnvelope) Write(writer io.Writer, value TripleRatchetStateAndEnvelope) {
|
||||
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
|
||||
FfiConverterStringINSTANCE.Write(writer, value.Envelope)
|
||||
}
|
||||
|
||||
type FfiDestroyerTypeTripleRatchetStateAndEnvelope struct{}
|
||||
|
||||
func (_ FfiDestroyerTypeTripleRatchetStateAndEnvelope) Destroy(value TripleRatchetStateAndEnvelope) {
|
||||
value.Destroy()
|
||||
}
|
||||
|
||||
type TripleRatchetStateAndMessage struct {
|
||||
RatchetState string
|
||||
Message []uint8
|
||||
}
|
||||
|
||||
func (r *TripleRatchetStateAndMessage) Destroy() {
|
||||
FfiDestroyerString{}.Destroy(r.RatchetState)
|
||||
FfiDestroyerSequenceUint8{}.Destroy(r.Message)
|
||||
}
|
||||
|
||||
type FfiConverterTypeTripleRatchetStateAndMessage struct{}
|
||||
|
||||
var FfiConverterTypeTripleRatchetStateAndMessageINSTANCE = FfiConverterTypeTripleRatchetStateAndMessage{}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMessage) Lift(rb RustBufferI) TripleRatchetStateAndMessage {
|
||||
return LiftFromRustBuffer[TripleRatchetStateAndMessage](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMessage) Read(reader io.Reader) TripleRatchetStateAndMessage {
|
||||
return TripleRatchetStateAndMessage{
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
FfiConverterSequenceUint8INSTANCE.Read(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMessage) Lower(value TripleRatchetStateAndMessage) RustBuffer {
|
||||
return LowerIntoRustBuffer[TripleRatchetStateAndMessage](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMessage) Write(writer io.Writer, value TripleRatchetStateAndMessage) {
|
||||
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
|
||||
FfiConverterSequenceUint8INSTANCE.Write(writer, value.Message)
|
||||
}
|
||||
|
||||
type FfiDestroyerTypeTripleRatchetStateAndMessage struct{}
|
||||
|
||||
func (_ FfiDestroyerTypeTripleRatchetStateAndMessage) Destroy(value TripleRatchetStateAndMessage) {
|
||||
value.Destroy()
|
||||
}
|
||||
|
||||
type TripleRatchetStateAndMetadata struct {
|
||||
RatchetState string
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
func (r *TripleRatchetStateAndMetadata) Destroy() {
|
||||
FfiDestroyerString{}.Destroy(r.RatchetState)
|
||||
FfiDestroyerMapStringString{}.Destroy(r.Metadata)
|
||||
}
|
||||
|
||||
type FfiConverterTypeTripleRatchetStateAndMetadata struct{}
|
||||
|
||||
var FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE = FfiConverterTypeTripleRatchetStateAndMetadata{}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Lift(rb RustBufferI) TripleRatchetStateAndMetadata {
|
||||
return LiftFromRustBuffer[TripleRatchetStateAndMetadata](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Read(reader io.Reader) TripleRatchetStateAndMetadata {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
FfiConverterStringINSTANCE.Read(reader),
|
||||
FfiConverterMapStringStringINSTANCE.Read(reader),
|
||||
}
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Lower(value TripleRatchetStateAndMetadata) RustBuffer {
|
||||
return LowerIntoRustBuffer[TripleRatchetStateAndMetadata](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterTypeTripleRatchetStateAndMetadata) Write(writer io.Writer, value TripleRatchetStateAndMetadata) {
|
||||
FfiConverterStringINSTANCE.Write(writer, value.RatchetState)
|
||||
FfiConverterMapStringStringINSTANCE.Write(writer, value.Metadata)
|
||||
}
|
||||
|
||||
type FfiDestroyerTypeTripleRatchetStateAndMetadata struct{}
|
||||
|
||||
func (_ FfiDestroyerTypeTripleRatchetStateAndMetadata) Destroy(value TripleRatchetStateAndMetadata) {
|
||||
value.Destroy()
|
||||
}
|
||||
|
||||
type FfiConverterSequenceUint8 struct{}
|
||||
|
||||
var FfiConverterSequenceUint8INSTANCE = FfiConverterSequenceUint8{}
|
||||
|
||||
func (c FfiConverterSequenceUint8) Lift(rb RustBufferI) []uint8 {
|
||||
return LiftFromRustBuffer[[]uint8](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceUint8) Read(reader io.Reader) []uint8 {
|
||||
length := readInt32(reader)
|
||||
if length == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]uint8, 0, length)
|
||||
for i := int32(0); i < length; i++ {
|
||||
result = append(result, FfiConverterUint8INSTANCE.Read(reader))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceUint8) Lower(value []uint8) RustBuffer {
|
||||
return LowerIntoRustBuffer[[]uint8](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceUint8) Write(writer io.Writer, value []uint8) {
|
||||
if len(value) > math.MaxInt32 {
|
||||
panic("[]uint8 is too large to fit into Int32")
|
||||
}
|
||||
|
||||
writeInt32(writer, int32(len(value)))
|
||||
for _, item := range value {
|
||||
FfiConverterUint8INSTANCE.Write(writer, item)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiDestroyerSequenceUint8 struct{}
|
||||
|
||||
func (FfiDestroyerSequenceUint8) Destroy(sequence []uint8) {
|
||||
for _, value := range sequence {
|
||||
FfiDestroyerUint8{}.Destroy(value)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiConverterSequenceSequenceUint8 struct{}
|
||||
|
||||
var FfiConverterSequenceSequenceUint8INSTANCE = FfiConverterSequenceSequenceUint8{}
|
||||
|
||||
func (c FfiConverterSequenceSequenceUint8) Lift(rb RustBufferI) [][]uint8 {
|
||||
return LiftFromRustBuffer[[][]uint8](c, rb)
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceSequenceUint8) Read(reader io.Reader) [][]uint8 {
|
||||
length := readInt32(reader)
|
||||
if length == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([][]uint8, 0, length)
|
||||
for i := int32(0); i < length; i++ {
|
||||
result = append(result, FfiConverterSequenceUint8INSTANCE.Read(reader))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceSequenceUint8) Lower(value [][]uint8) RustBuffer {
|
||||
return LowerIntoRustBuffer[[][]uint8](c, value)
|
||||
}
|
||||
|
||||
func (c FfiConverterSequenceSequenceUint8) Write(writer io.Writer, value [][]uint8) {
|
||||
if len(value) > math.MaxInt32 {
|
||||
panic("[][]uint8 is too large to fit into Int32")
|
||||
}
|
||||
|
||||
writeInt32(writer, int32(len(value)))
|
||||
for _, item := range value {
|
||||
FfiConverterSequenceUint8INSTANCE.Write(writer, item)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiDestroyerSequenceSequenceUint8 struct{}
|
||||
|
||||
func (FfiDestroyerSequenceSequenceUint8) Destroy(sequence [][]uint8) {
|
||||
for _, value := range sequence {
|
||||
FfiDestroyerSequenceUint8{}.Destroy(value)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiConverterMapStringString struct{}
|
||||
|
||||
var FfiConverterMapStringStringINSTANCE = FfiConverterMapStringString{}
|
||||
|
||||
func (c FfiConverterMapStringString) Lift(rb RustBufferI) map[string]string {
|
||||
return LiftFromRustBuffer[map[string]string](c, rb)
|
||||
}
|
||||
|
||||
func (_ FfiConverterMapStringString) Read(reader io.Reader) map[string]string {
|
||||
result := make(map[string]string)
|
||||
length := readInt32(reader)
|
||||
for i := int32(0); i < length; i++ {
|
||||
key := FfiConverterStringINSTANCE.Read(reader)
|
||||
value := FfiConverterStringINSTANCE.Read(reader)
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c FfiConverterMapStringString) Lower(value map[string]string) RustBuffer {
|
||||
return LowerIntoRustBuffer[map[string]string](c, value)
|
||||
}
|
||||
|
||||
func (_ FfiConverterMapStringString) Write(writer io.Writer, mapValue map[string]string) {
|
||||
if len(mapValue) > math.MaxInt32 {
|
||||
panic("map[string]string is too large to fit into Int32")
|
||||
}
|
||||
|
||||
writeInt32(writer, int32(len(mapValue)))
|
||||
for key, value := range mapValue {
|
||||
FfiConverterStringINSTANCE.Write(writer, key)
|
||||
FfiConverterStringINSTANCE.Write(writer, value)
|
||||
}
|
||||
}
|
||||
|
||||
type FfiDestroyerMapStringString struct{}
|
||||
|
||||
func (_ FfiDestroyerMapStringString) Destroy(mapValue map[string]string) {
|
||||
for key, value := range mapValue {
|
||||
FfiDestroyerString{}.Destroy(key)
|
||||
FfiDestroyerString{}.Destroy(value)
|
||||
}
|
||||
}
|
||||
|
||||
func DoubleRatchetDecrypt(ratchetStateAndEnvelope DoubleRatchetStateAndEnvelope) DoubleRatchetStateAndMessage {
|
||||
return FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_double_ratchet_decrypt(FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE.Lower(ratchetStateAndEnvelope), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func DoubleRatchetEncrypt(ratchetStateAndMessage DoubleRatchetStateAndMessage) DoubleRatchetStateAndEnvelope {
|
||||
return FfiConverterTypeDoubleRatchetStateAndEnvelopeINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_double_ratchet_encrypt(FfiConverterTypeDoubleRatchetStateAndMessageINSTANCE.Lower(ratchetStateAndMessage), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func NewDoubleRatchet(sessionKey []uint8, sendingHeaderKey []uint8, nextReceivingHeaderKey []uint8, isSender bool, sendingEphemeralPrivateKey []uint8, receivingEphemeralKey []uint8) string {
|
||||
return FfiConverterStringINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_new_double_ratchet(FfiConverterSequenceUint8INSTANCE.Lower(sessionKey), FfiConverterSequenceUint8INSTANCE.Lower(sendingHeaderKey), FfiConverterSequenceUint8INSTANCE.Lower(nextReceivingHeaderKey), FfiConverterBoolINSTANCE.Lower(isSender), FfiConverterSequenceUint8INSTANCE.Lower(sendingEphemeralPrivateKey), FfiConverterSequenceUint8INSTANCE.Lower(receivingEphemeralKey), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func NewTripleRatchet(peers [][]uint8, peerKey []uint8, identityKey []uint8, signedPreKey []uint8, threshold uint64, asyncDkgRatchet bool) TripleRatchetStateAndMetadata {
|
||||
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_new_triple_ratchet(FfiConverterSequenceSequenceUint8INSTANCE.Lower(peers), FfiConverterSequenceUint8INSTANCE.Lower(peerKey), FfiConverterSequenceUint8INSTANCE.Lower(identityKey), FfiConverterSequenceUint8INSTANCE.Lower(signedPreKey), FfiConverterUint64INSTANCE.Lower(threshold), FfiConverterBoolINSTANCE.Lower(asyncDkgRatchet), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetDecrypt(ratchetStateAndEnvelope TripleRatchetStateAndEnvelope) TripleRatchetStateAndMessage {
|
||||
return FfiConverterTypeTripleRatchetStateAndMessageINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_decrypt(FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE.Lower(ratchetStateAndEnvelope), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetEncrypt(ratchetStateAndMessage TripleRatchetStateAndMessage) TripleRatchetStateAndEnvelope {
|
||||
return FfiConverterTypeTripleRatchetStateAndEnvelopeINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_encrypt(FfiConverterTypeTripleRatchetStateAndMessageINSTANCE.Lower(ratchetStateAndMessage), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetInitRound1(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
|
||||
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_init_round_1(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetInitRound2(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
|
||||
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_init_round_2(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetInitRound3(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
|
||||
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_init_round_3(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
|
||||
}))
|
||||
}
|
||||
|
||||
func TripleRatchetInitRound4(ratchetStateAndMetadata TripleRatchetStateAndMetadata) TripleRatchetStateAndMetadata {
|
||||
return FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lift(rustCall(func(_uniffiStatus *C.RustCallStatus) RustBufferI {
|
||||
return C.uniffi_channel_fn_func_triple_ratchet_init_round_4(FfiConverterTypeTripleRatchetStateAndMetadataINSTANCE.Lower(ratchetStateAndMetadata), _uniffiStatus)
|
||||
}))
|
||||
}
|
475
channel/generated/channel/channel.h
Normal file
475
channel/generated/channel/channel.h
Normal file
@ -0,0 +1,475 @@
|
||||
|
||||
|
||||
// This file was autogenerated by some hot garbage in the `uniffi` crate.
|
||||
// Trust me, you don't want to mess with it!
|
||||
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// The following structs are used to implement the lowest level
|
||||
// of the FFI, and thus useful to multiple uniffied crates.
|
||||
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
|
||||
#ifdef UNIFFI_SHARED_H
|
||||
// We also try to prevent mixing versions of shared uniffi header structs.
|
||||
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V6
|
||||
#ifndef UNIFFI_SHARED_HEADER_V6
|
||||
#error Combining helper code from multiple versions of uniffi is not supported
|
||||
#endif // ndef UNIFFI_SHARED_HEADER_V6
|
||||
#else
|
||||
#define UNIFFI_SHARED_H
|
||||
#define UNIFFI_SHARED_HEADER_V6
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
|
||||
|
||||
typedef struct RustBuffer {
|
||||
int32_t capacity;
|
||||
int32_t len;
|
||||
uint8_t *data;
|
||||
} RustBuffer;
|
||||
|
||||
typedef int32_t (*ForeignCallback)(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *);
|
||||
|
||||
// Task defined in Rust that Go executes
|
||||
typedef void (*RustTaskCallback)(const void *, int8_t);
|
||||
|
||||
// Callback to execute Rust tasks using a Go routine
|
||||
//
|
||||
// Args:
|
||||
// executor: ForeignExecutor lowered into a uint64_t value
|
||||
// delay: Delay in MS
|
||||
// task: RustTaskCallback to call
|
||||
// task_data: data to pass the task callback
|
||||
typedef int8_t (*ForeignExecutorCallback)(uint64_t, uint32_t, RustTaskCallback, void *);
|
||||
|
||||
typedef struct ForeignBytes {
|
||||
int32_t len;
|
||||
const uint8_t *data;
|
||||
} ForeignBytes;
|
||||
|
||||
// Error definitions
|
||||
typedef struct RustCallStatus {
|
||||
int8_t code;
|
||||
RustBuffer errorBuf;
|
||||
} RustCallStatus;
|
||||
|
||||
// Continuation callback for UniFFI Futures
|
||||
typedef void (*RustFutureContinuation)(void * , int8_t);
|
||||
|
||||
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
|
||||
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
|
||||
#endif // def UNIFFI_SHARED_H
|
||||
|
||||
// Needed because we can't execute the callback directly from go.
|
||||
void cgo_rust_task_callback_bridge_channel(RustTaskCallback, const void *, int8_t);
|
||||
|
||||
int8_t uniffiForeignExecutorCallbackchannel(uint64_t, uint32_t, RustTaskCallback, void*);
|
||||
|
||||
void uniffiFutureContinuationCallbackchannel(void*, int8_t);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_double_ratchet_decrypt(
|
||||
RustBuffer ratchet_state_and_envelope,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_double_ratchet_encrypt(
|
||||
RustBuffer ratchet_state_and_message,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_new_double_ratchet(
|
||||
RustBuffer session_key,
|
||||
RustBuffer sending_header_key,
|
||||
RustBuffer next_receiving_header_key,
|
||||
int8_t is_sender,
|
||||
RustBuffer sending_ephemeral_private_key,
|
||||
RustBuffer receiving_ephemeral_key,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_new_triple_ratchet(
|
||||
RustBuffer peers,
|
||||
RustBuffer peer_key,
|
||||
RustBuffer identity_key,
|
||||
RustBuffer signed_pre_key,
|
||||
uint64_t threshold,
|
||||
int8_t async_dkg_ratchet,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_decrypt(
|
||||
RustBuffer ratchet_state_and_envelope,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_encrypt(
|
||||
RustBuffer ratchet_state_and_message,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_1(
|
||||
RustBuffer ratchet_state_and_metadata,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_2(
|
||||
RustBuffer ratchet_state_and_metadata,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_3(
|
||||
RustBuffer ratchet_state_and_metadata,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer uniffi_channel_fn_func_triple_ratchet_init_round_4(
|
||||
RustBuffer ratchet_state_and_metadata,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer ffi_channel_rustbuffer_alloc(
|
||||
int32_t size,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer ffi_channel_rustbuffer_from_bytes(
|
||||
ForeignBytes bytes,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rustbuffer_free(
|
||||
RustBuffer buf,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer ffi_channel_rustbuffer_reserve(
|
||||
RustBuffer buf,
|
||||
int32_t additional,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_continuation_callback_set(
|
||||
RustFutureContinuation callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_u8(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_u8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_u8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint8_t ffi_channel_rust_future_complete_u8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_i8(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_i8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_i8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
int8_t ffi_channel_rust_future_complete_i8(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_u16(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_u16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_u16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t ffi_channel_rust_future_complete_u16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_i16(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_i16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_i16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
int16_t ffi_channel_rust_future_complete_i16(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_u32(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_u32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_u32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint32_t ffi_channel_rust_future_complete_u32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_i32(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_i32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_i32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
int32_t ffi_channel_rust_future_complete_i32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_u64(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_u64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_u64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint64_t ffi_channel_rust_future_complete_u64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_i64(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_i64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_i64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
int64_t ffi_channel_rust_future_complete_i64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_f32(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_f32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_f32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
float ffi_channel_rust_future_complete_f32(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_f64(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_f64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_f64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
double ffi_channel_rust_future_complete_f64(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_pointer(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_pointer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_pointer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void* ffi_channel_rust_future_complete_pointer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_rust_buffer(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_rust_buffer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_rust_buffer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
RustBuffer ffi_channel_rust_future_complete_rust_buffer(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_poll_void(
|
||||
void* handle,
|
||||
void* uniffi_callback,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_cancel_void(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_free_void(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
void ffi_channel_rust_future_complete_void(
|
||||
void* handle,
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_double_ratchet_decrypt(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_double_ratchet_encrypt(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_new_double_ratchet(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_new_triple_ratchet(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_decrypt(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_encrypt(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_1(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_2(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_3(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint16_t uniffi_channel_checksum_func_triple_ratchet_init_round_4(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
uint32_t ffi_channel_uniffi_contract_version(
|
||||
RustCallStatus* out_status
|
||||
);
|
||||
|
||||
|
||||
|
17
channel/go.mod
Normal file
17
channel/go.mod
Normal file
@ -0,0 +1,17 @@
|
||||
module source.quilibrium.com/quilibrium/monorepo/channel
|
||||
|
||||
go 1.20
|
||||
|
||||
// A necessary hack until source.quilibrium.com is open to all
|
||||
replace source.quilibrium.com/quilibrium/monorepo/nekryptology => ../nekryptology
|
||||
|
||||
require github.com/stretchr/testify v1.9.0
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
source.quilibrium.com/quilibrium/monorepo/nekryptology v0.0.0-00010101000000-000000000000 // indirect
|
||||
)
|
13
channel/go.sum
Normal file
13
channel/go.sum
Normal file
@ -0,0 +1,13 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
17
channel/test.sh
Executable file
17
channel/test.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euxo pipefail
|
||||
|
||||
# Run tests for the channel package. Takes care of linking the native Channel library.
|
||||
# Assumes that the Channel library has been built by running the generate.sh script in the same directory.
|
||||
|
||||
ROOT_DIR="${ROOT_DIR:-$( cd "$(dirname "$(realpath "$( dirname "${BASH_SOURCE[0]}" )")")" >/dev/null 2>&1 && pwd )}"
|
||||
|
||||
NODE_DIR="$ROOT_DIR/channel"
|
||||
BINARIES_DIR="$ROOT_DIR/target/release"
|
||||
|
||||
# Link the native Channel library and execute tests
|
||||
pushd "$NODE_DIR" > /dev/null
|
||||
CGO_LDFLAGS="-L$BINARIES_DIR -lchannel -ldl" \
|
||||
CGO_ENABLED=1 \
|
||||
GOEXPERIMENT=arenas \
|
||||
go test "$@"
|
@ -363,7 +363,7 @@ pub fn prove_raw(
|
||||
subz = big::BIG::modadd(&subz, &big::BIG::modneg(&z, &big::BIG::new_ints(&rom::CURVE_ORDER)), &big::BIG::new_ints(&rom::CURVE_ORDER));
|
||||
let mut subzinv = subz.clone();
|
||||
subzinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
|
||||
let mut o = big::BIG::new_int(1);
|
||||
let o = big::BIG::new_int(1);
|
||||
let mut oinv = o.clone();
|
||||
oinv.invmodp(&big::BIG::new_ints(&rom::CURVE_ORDER));
|
||||
let divisors: Vec<big::BIG> = vec![
|
||||
@ -436,7 +436,29 @@ pub fn verify_raw(
|
||||
let y = big::BIG::frombytes(data);
|
||||
|
||||
let c = ecp::ECP::frombytes(commit);
|
||||
if c.is_infinity() || c.equals(&ecp::ECP::generator()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let p = ecp::ECP::frombytes(proof);
|
||||
if p.is_infinity() || p.equals(&ecp::ECP::generator()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if poly_size > 1024 {
|
||||
let mut xc = c.clone();
|
||||
xc.sub(&bls::singleton().FFTBLS48581[&poly_size][index as usize].clone().mul(&y));
|
||||
let mut check = c.clone();
|
||||
check.neg();
|
||||
let yp = &bls::singleton().CeremonyBLS48581G2[1].clone().mul(&y);
|
||||
let mut r = pair8::initmp();
|
||||
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &check);
|
||||
pair8::another(&mut r, &yp, &bls::singleton().FFTBLS48581[&poly_size][index as usize]);
|
||||
pair8::another(&mut r, &bls::singleton().CeremonyBLS48581G2[1], &xc);
|
||||
let mut v = pair8::miller(&mut r);
|
||||
v = pair8::fexp(&v);
|
||||
return v.isunity();
|
||||
}
|
||||
|
||||
return verify(
|
||||
&c,
|
||||
@ -473,4 +495,4 @@ mod tests {
|
||||
assert!(points[i].equals(&sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
crates/channel/Cargo.lock
generated
Normal file
7
crates/channel/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "channel"
|
||||
version = "0.1.0"
|
34
crates/channel/Cargo.toml
Normal file
34
crates/channel/Cargo.toml
Normal file
@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "channel"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib"]
|
||||
name = "channel"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.22.1"
|
||||
hex = "0.4.3"
|
||||
serde_json = "1.0.117"
|
||||
ed448-goldilocks-plus = "0.11.2"
|
||||
rand = "0.8.5"
|
||||
sha2 = "0.10.8"
|
||||
hkdf = "0.12.4"
|
||||
aes-gcm = "0.10.3"
|
||||
thiserror = "1.0.63"
|
||||
hmac = "0.12.1"
|
||||
serde = "1.0.208"
|
||||
lazy_static = "1.5.0"
|
||||
uniffi = { version= "0.25", features = ["cli"]}
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4", features = ["html_reports"] }
|
||||
rand = "0.8.5"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.25", features = [ "build" ] }
|
||||
|
||||
[[bench]]
|
||||
name = "bench_channel"
|
||||
harness = false
|
9
crates/channel/benches/bench_channel.rs
Normal file
9
crates/channel/benches/bench_channel.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use rand::Rng;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
5
crates/channel/build.rs
Normal file
5
crates/channel/build.rs
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
uniffi::generate_scaffolding("src/lib.udl").expect("uniffi generation failed");
|
||||
}
|
991
crates/channel/src/lib.rs
Normal file
991
crates/channel/src/lib.rs
Normal file
@ -0,0 +1,991 @@
|
||||
use base64::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ed448_goldilocks_plus::{elliptic_curve::group::GroupEncoding, EdwardsPoint, Scalar};
|
||||
use protocols::{doubleratchet::{DoubleRatchetParticipant, P2PChannelEnvelope}, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
|
||||
|
||||
pub(crate) mod protocols;
|
||||
|
||||
pub struct DoubleRatchetStateAndEnvelope {
|
||||
pub ratchet_state: String,
|
||||
pub envelope: String,
|
||||
}
|
||||
|
||||
pub struct DoubleRatchetStateAndMessage {
|
||||
pub ratchet_state: String,
|
||||
pub message: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct TripleRatchetStateAndMetadata {
|
||||
pub ratchet_state: String,
|
||||
pub metadata: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub struct TripleRatchetStateAndEnvelope {
|
||||
pub ratchet_state: String,
|
||||
pub envelope: String,
|
||||
}
|
||||
|
||||
pub struct TripleRatchetStateAndMessage {
|
||||
pub ratchet_state: String,
|
||||
pub message: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn new_double_ratchet(session_key: &Vec<u8>, sending_header_key: &Vec<u8>, next_receiving_header_key: &Vec<u8>, is_sender: bool, sending_ephemeral_private_key: &Vec<u8>, receiving_ephemeral_key: &Vec<u8>) -> String {
|
||||
if sending_ephemeral_private_key.len() != 56 {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
if receiving_ephemeral_key.len() != 57 {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
let mut sending_ephemeral_private_key_bytes = [0u8; 56];
|
||||
sending_ephemeral_private_key_bytes.copy_from_slice(&sending_ephemeral_private_key);
|
||||
|
||||
let mut receiving_ephemeral_key_bytes = [0u8; 57];
|
||||
receiving_ephemeral_key_bytes.copy_from_slice(&receiving_ephemeral_key);
|
||||
|
||||
let sending_key = Scalar::from_bytes(&sending_ephemeral_private_key_bytes.into());
|
||||
let receiving_key = EdwardsPoint::from_bytes(&receiving_ephemeral_key_bytes.into()).into_option();
|
||||
if receiving_key.is_none() {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
let participant = DoubleRatchetParticipant::new(
|
||||
&session_key,
|
||||
&sending_header_key,
|
||||
&next_receiving_header_key,
|
||||
true,
|
||||
sending_key,
|
||||
receiving_key.unwrap(),
|
||||
);
|
||||
|
||||
if participant.is_err() {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
let json = participant.unwrap().to_json();
|
||||
if json.is_err() {
|
||||
return "".to_string();
|
||||
}
|
||||
|
||||
return json.unwrap();
|
||||
}
|
||||
|
||||
pub fn double_ratchet_encrypt(ratchet_state_and_message: DoubleRatchetStateAndMessage) -> DoubleRatchetStateAndEnvelope {
|
||||
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
|
||||
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
|
||||
|
||||
if participant.is_err() {
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut dr = participant.unwrap();
|
||||
let envelope = dr.ratchet_encrypt(&ratchet_state_and_message.message);
|
||||
|
||||
if envelope.is_err() {
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let participant_json = dr.to_json();
|
||||
if participant_json.is_err() {
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let envelope_json = envelope.unwrap().to_json();
|
||||
if envelope_json.is_err() {
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
return DoubleRatchetStateAndEnvelope{
|
||||
ratchet_state: participant_json.unwrap(),
|
||||
envelope: envelope_json.unwrap(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn double_ratchet_decrypt(ratchet_state_and_envelope: DoubleRatchetStateAndEnvelope) -> DoubleRatchetStateAndMessage {
|
||||
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
|
||||
let participant = DoubleRatchetParticipant::from_json(ratchet_state.clone());
|
||||
let envelope = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
|
||||
|
||||
if participant.is_err() || envelope.is_err() {
|
||||
return DoubleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let mut dr = participant.unwrap();
|
||||
let message = dr.ratchet_decrypt(&envelope.unwrap());
|
||||
|
||||
if message.is_err() {
|
||||
return DoubleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let participant_json = dr.to_json();
|
||||
if participant_json.is_err() {
|
||||
return DoubleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
return DoubleRatchetStateAndMessage{
|
||||
ratchet_state: participant_json.unwrap(),
|
||||
message: message.unwrap(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn new_triple_ratchet(peers: &Vec<Vec<u8>>, peer_key: &Vec<u8>, identity_key: &Vec<u8>, signed_pre_key: &Vec<u8>, threshold: u64, async_dkg_ratchet: bool) -> TripleRatchetStateAndMetadata {
|
||||
if peer_key.len() != 56 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
if identity_key.len() != 56 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
if signed_pre_key.len() != 56 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
if peers.len() < 3 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
if threshold > peers.len() as u64 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut peer_key_bytes = [0u8; 56];
|
||||
peer_key_bytes.copy_from_slice(&peer_key);
|
||||
|
||||
let mut identity_key_bytes = [0u8; 56];
|
||||
identity_key_bytes.copy_from_slice(&identity_key);
|
||||
|
||||
let mut signed_pre_key_bytes = [0u8; 56];
|
||||
signed_pre_key_bytes.copy_from_slice(&signed_pre_key);
|
||||
|
||||
let peer_key_scalar = Scalar::from_bytes(&peer_key_bytes.into());
|
||||
let identity_key_scalar = Scalar::from_bytes(&identity_key_bytes.into());
|
||||
let signed_pre_key_scalar = Scalar::from_bytes(&signed_pre_key_bytes.into());
|
||||
let mut peerinfos = Vec::<PeerInfo>::new();
|
||||
for pk in peers.iter() {
|
||||
if pk.len() != 171 {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
peerinfos.push(PeerInfo{
|
||||
public_key: pk[..57].into(),
|
||||
identity_public_key: pk[57..114].into(),
|
||||
signed_pre_public_key: pk[114..].into(),
|
||||
});
|
||||
}
|
||||
|
||||
let participant = TripleRatchetParticipant::new(
|
||||
&peerinfos,
|
||||
peer_key_scalar,
|
||||
identity_key_scalar,
|
||||
signed_pre_key_scalar,
|
||||
threshold as usize,
|
||||
async_dkg_ratchet,
|
||||
);
|
||||
|
||||
if participant.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let (tr, metadata) = participant.unwrap();
|
||||
|
||||
let participant_json = tr.to_json();
|
||||
|
||||
if participant_json.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: "".to_string(),
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata_json = match metadata_to_json(&String::from(""), metadata) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: participant_json.unwrap(),
|
||||
metadata: metadata_json,
|
||||
};
|
||||
}
|
||||
|
||||
fn metadata_to_json(ratchet_state: &String, metadata: HashMap<Vec<u8>, P2PChannelEnvelope>) -> Result<HashMap<String, String>, TripleRatchetStateAndMetadata> {
|
||||
let mut metadata_json = HashMap::<String, String>::new();
|
||||
for (k,v) in metadata {
|
||||
let env = v.to_json();
|
||||
if env.is_err() {
|
||||
return Err(TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state.to_string(),
|
||||
metadata: HashMap::new(),
|
||||
});
|
||||
}
|
||||
|
||||
metadata_json.insert(BASE64_STANDARD.encode(k), env.unwrap());
|
||||
}
|
||||
Ok(metadata_json)
|
||||
}
|
||||
|
||||
fn json_to_metadata(ratchet_state_and_metadata: TripleRatchetStateAndMetadata, ratchet_state: &String) -> Result<HashMap<Vec<u8>, P2PChannelEnvelope>, TripleRatchetStateAndMetadata> {
|
||||
let mut metadata = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
||||
for (k,v) in ratchet_state_and_metadata.metadata {
|
||||
let env = P2PChannelEnvelope::from_json(v);
|
||||
let kb = BASE64_STANDARD.decode(k);
|
||||
if env.is_err() || kb.is_err() {
|
||||
return Err(TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state.clone(),
|
||||
metadata: HashMap::new(),
|
||||
});
|
||||
}
|
||||
|
||||
metadata.insert(kb.unwrap(), env.unwrap());
|
||||
}
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_init_round_1(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
||||
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let result = trp.initialize(&metadata);
|
||||
if result.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata = result.unwrap();
|
||||
let metadata_json = match metadata_to_json(&ratchet_state, metadata) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: json.unwrap(),
|
||||
metadata: metadata_json,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_init_round_2(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
||||
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
||||
for (k, v) in metadata {
|
||||
let r = trp.receive_poly_frag(&k, &v);
|
||||
if r.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let opt = r.unwrap();
|
||||
if opt.is_some() {
|
||||
result = opt.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: json.unwrap(),
|
||||
metadata: metadata_json,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_init_round_3(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
||||
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
||||
for (k, v) in metadata {
|
||||
let r = trp.receive_commitment(&k, &v);
|
||||
if r.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let opt = r.unwrap();
|
||||
if opt.is_some() {
|
||||
result = opt.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: json.unwrap(),
|
||||
metadata: metadata_json,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_init_round_4(ratchet_state_and_metadata: TripleRatchetStateAndMetadata) -> TripleRatchetStateAndMetadata {
|
||||
let ratchet_state = ratchet_state_and_metadata.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
let metadata = match json_to_metadata(ratchet_state_and_metadata, &ratchet_state) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let mut result = HashMap::<Vec<u8>, P2PChannelEnvelope>::new();
|
||||
for (k, v) in metadata {
|
||||
let r = trp.recombine(&k, &v);
|
||||
if r.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let metadata_json = match metadata_to_json(&ratchet_state, result) {
|
||||
Ok(value) => value,
|
||||
Err(value) => return value,
|
||||
};
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: ratchet_state,
|
||||
metadata: HashMap::new(),
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndMetadata{
|
||||
ratchet_state: json.unwrap(),
|
||||
metadata: metadata_json,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_encrypt(ratchet_state_and_message: TripleRatchetStateAndMessage) -> TripleRatchetStateAndEnvelope {
|
||||
let ratchet_state = ratchet_state_and_message.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let result = trp.ratchet_encrypt(&ratchet_state_and_message.message);
|
||||
|
||||
if result.is_err() {
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let envelope = result.unwrap();
|
||||
let envelope_json = envelope.to_json();
|
||||
|
||||
if envelope_json.is_err() {
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
ratchet_state: ratchet_state,
|
||||
envelope: "".to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndEnvelope{
|
||||
ratchet_state: json.unwrap(),
|
||||
envelope: envelope_json.unwrap(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn triple_ratchet_decrypt(ratchet_state_and_envelope: TripleRatchetStateAndEnvelope) -> TripleRatchetStateAndMessage {
|
||||
let ratchet_state = ratchet_state_and_envelope.ratchet_state.clone();
|
||||
let tr = TripleRatchetParticipant::from_json(&ratchet_state);
|
||||
if tr.is_err() {
|
||||
return TripleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let mut trp = tr.unwrap();
|
||||
let env = P2PChannelEnvelope::from_json(ratchet_state_and_envelope.envelope);
|
||||
if env.is_err() {
|
||||
return TripleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let result = trp.ratchet_decrypt(&env.unwrap());
|
||||
|
||||
if result.is_err() {
|
||||
return TripleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
let message = result.unwrap().0;
|
||||
|
||||
let json = trp.to_json();
|
||||
if json.is_err() {
|
||||
return TripleRatchetStateAndMessage{
|
||||
ratchet_state: ratchet_state,
|
||||
message: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
return TripleRatchetStateAndMessage{
|
||||
ratchet_state: json.unwrap(),
|
||||
message: message,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
use ed448_goldilocks_plus::{Scalar, elliptic_curve::Group, EdwardsPoint};
|
||||
use protocols::{doubleratchet::P2PChannelEnvelope, tripleratchet::{PeerInfo, TripleRatchetParticipant}};
|
||||
|
||||
#[test]
|
||||
fn test_four_party_triple_ratchet_communication() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
||||
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
||||
|
||||
let mut peer_infos: Vec<PeerInfo> = keys
|
||||
.iter()
|
||||
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
||||
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// mirror the internal order so we can use by index:
|
||||
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
||||
|
||||
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
||||
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
|
||||
for i in 0..4 {
|
||||
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
||||
.filter(|&(j, _)| j != i)
|
||||
.map(|(_, peer)| peer.clone())
|
||||
.collect();
|
||||
|
||||
let (participant, init_msg) = TripleRatchetParticipant::new(
|
||||
&other_peers,
|
||||
keys[i].0.clone(),
|
||||
keys[i].1.clone(),
|
||||
keys[i].2.clone(),
|
||||
3,
|
||||
false,
|
||||
).unwrap();
|
||||
|
||||
participants.push(participant);
|
||||
|
||||
for (j, env) in init_msg.iter() {
|
||||
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange initial messages and get frags:
|
||||
for i in 0..4 {
|
||||
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
||||
for (j, env) in result.iter() {
|
||||
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange frags and receive commitments once all frags have been distributed:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
||||
for (j, env) in out.iter() {
|
||||
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange commitments and produce reveals:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
||||
for (j, env) in reveal_msg.iter() {
|
||||
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect reveals and confirm zkpoks are valid, produce group key:
|
||||
for i in 0..4 {
|
||||
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
participants[i].recombine(j, &env.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Test sending and receiving messages
|
||||
let test_messages = [
|
||||
"hello there",
|
||||
"general kenobi",
|
||||
"you are a bold one",
|
||||
"*mechanical laughter*",
|
||||
];
|
||||
|
||||
for (i, message) in test_messages.iter().enumerate() {
|
||||
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
||||
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
for i in 0..4 {
|
||||
let message1 = format!("test 1 {}", i + 1);
|
||||
let message2 = format!("test 2 {}", i + 1);
|
||||
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
||||
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
||||
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
||||
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
||||
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_four_party_triple_ratchet_communication_with_serialization_each_step() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
||||
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
||||
|
||||
let mut peer_infos: Vec<PeerInfo> = keys
|
||||
.iter()
|
||||
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
||||
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// mirror the internal order so we can use by index:
|
||||
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
||||
|
||||
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
||||
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
|
||||
for i in 0..4 {
|
||||
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
||||
.filter(|&(j, _)| j != i)
|
||||
.map(|(_, peer)| peer.clone())
|
||||
.collect();
|
||||
|
||||
let (participant, init_msg) = TripleRatchetParticipant::new(
|
||||
&other_peers,
|
||||
keys[i].0.clone(),
|
||||
keys[i].1.clone(),
|
||||
keys[i].2.clone(),
|
||||
3,
|
||||
false,
|
||||
).unwrap();
|
||||
|
||||
for (j, env) in init_msg.iter() {
|
||||
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
|
||||
let participant_json = participant.to_json();
|
||||
if participant_json.is_err() {
|
||||
panic!("bad json");
|
||||
}
|
||||
participants.push(TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap());
|
||||
}
|
||||
|
||||
// Exchange initial messages and get frags:
|
||||
for i in 0..4 {
|
||||
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
||||
for (j, env) in result.iter() {
|
||||
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// Exchange frags and receive commitments once all frags have been distributed:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
||||
for (j, env) in out.iter() {
|
||||
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// Exchange commitments and produce reveals:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
||||
for (j, env) in reveal_msg.iter() {
|
||||
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
|
||||
// Collect reveals and confirm zkpoks are valid, produce group key:
|
||||
for i in 0..4 {
|
||||
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
participants[i].recombine(j, &env.clone()).unwrap();
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Test sending and receiving messages
|
||||
let test_messages = [
|
||||
"hello there",
|
||||
"general kenobi",
|
||||
"you are a bold one",
|
||||
"*mechanical laughter*",
|
||||
];
|
||||
|
||||
for (i, message) in test_messages.iter().enumerate() {
|
||||
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
||||
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
for i in 0..4 {
|
||||
let message1 = format!("test 1 {}", i + 1);
|
||||
let message2 = format!("test 2 {}", i + 1);
|
||||
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
||||
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
||||
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
||||
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
||||
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
|
||||
let participant_json = participants[i].to_json();
|
||||
participants[i] = TripleRatchetParticipant::from_json(&participant_json.unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_four_party_async_triple_ratchet_communication() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut keys: Vec<(Scalar, Scalar, Scalar)> = (0..4)
|
||||
.map(|_| (Scalar::random(&mut rng), Scalar::random(&mut rng), Scalar::random(&mut rng)))
|
||||
.collect();
|
||||
|
||||
keys.sort_by(|a, b| (a.0 * EdwardsPoint::generator()).compress().to_bytes().cmp(&(b.0 * EdwardsPoint::generator()).compress().to_bytes()));
|
||||
|
||||
let mut peer_infos: Vec<PeerInfo> = keys
|
||||
.iter()
|
||||
.map(|(peer_key, identity_key, signed_pre_key)| PeerInfo {
|
||||
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// mirror the internal order so we can use by index:
|
||||
peer_infos.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
||||
|
||||
let mut participants: Vec<TripleRatchetParticipant> = Vec::new();
|
||||
let mut init_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut frag_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut commitment_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
let mut reveal_messages: HashMap<Vec<u8>, HashMap<Vec<u8>, P2PChannelEnvelope>> = HashMap::new();
|
||||
|
||||
for i in 0..4 {
|
||||
init_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
frag_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
commitment_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
reveal_messages.insert(peer_infos[i].public_key.clone(), HashMap::new());
|
||||
}
|
||||
|
||||
for i in 0..4 {
|
||||
let other_peers: Vec<PeerInfo> = peer_infos.iter().enumerate()
|
||||
.filter(|&(j, _)| j != i)
|
||||
.map(|(_, peer)| peer.clone())
|
||||
.collect();
|
||||
|
||||
let (participant, init_msg) = TripleRatchetParticipant::new(
|
||||
&other_peers,
|
||||
keys[i].0.clone(),
|
||||
keys[i].1.clone(),
|
||||
keys[i].2.clone(),
|
||||
2,
|
||||
true,
|
||||
).unwrap();
|
||||
|
||||
participants.push(participant);
|
||||
|
||||
for (j, env) in init_msg.iter() {
|
||||
init_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange initial messages and get frags:
|
||||
for i in 0..4 {
|
||||
let result = participants[i].initialize(&init_messages[&peer_infos[i].public_key.clone()]).unwrap();
|
||||
for (j, env) in result.iter() {
|
||||
frag_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange frags and receive commitments once all frags have been distributed:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in frag_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(out) = participants[i].receive_poly_frag(&p, envelope).unwrap() {
|
||||
for (j, env) in out.iter() {
|
||||
commitment_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exchange commitments and produce reveals:
|
||||
for i in 0..4 {
|
||||
for (p, envelope) in commitment_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
if let Some(reveal_msg) = participants[i].receive_commitment(&p, envelope).unwrap() {
|
||||
for (j, env) in reveal_msg.iter() {
|
||||
reveal_messages.get_mut(j).unwrap().insert(peer_infos[i].public_key.clone(), env.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect reveals and confirm zkpoks are valid, produce group key:
|
||||
for i in 0..4 {
|
||||
for (j, env) in reveal_messages[&peer_infos[i].public_key.clone()].iter() {
|
||||
participants[i].recombine(j, &env.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Test sending and receiving messages
|
||||
let test_messages = [
|
||||
"hello there",
|
||||
"general kenobi",
|
||||
"you are a bold one",
|
||||
"*mechanical laughter*",
|
||||
];
|
||||
|
||||
for (i, message) in test_messages.iter().enumerate() {
|
||||
let encrypted = participants[i].ratchet_encrypt(message.as_bytes()).unwrap();
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted = participants[j].ratchet_decrypt(&encrypted).unwrap();
|
||||
assert_eq!(message.as_bytes(), decrypted.0.as_slice(), "Message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
for i in 0..4 {
|
||||
let message1 = format!("test 1 {}", i + 1);
|
||||
let message2 = format!("test 2 {}", i + 1);
|
||||
let encrypted1 = participants[i].ratchet_encrypt(message1.as_bytes()).unwrap();
|
||||
let encrypted2 = participants[i].ratchet_encrypt(message2.as_bytes()).unwrap();
|
||||
|
||||
for j in 0..4 {
|
||||
if i != j {
|
||||
let decrypted1 = participants[j].ratchet_decrypt(&encrypted1).unwrap();
|
||||
assert_eq!(message1.as_bytes(), decrypted1.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
let decrypted2 = participants[j].ratchet_decrypt(&encrypted2).unwrap();
|
||||
assert_eq!(message2.as_bytes(), decrypted2.0.as_slice(), "Round message decryption failed for Participant {}", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
crates/channel/src/lib.udl
Normal file
38
crates/channel/src/lib.udl
Normal file
@ -0,0 +1,38 @@
|
||||
namespace channel {
|
||||
string new_double_ratchet([ByRef] sequence<u8> session_key, [ByRef] sequence<u8> sending_header_key, [ByRef] sequence<u8> next_receiving_header_key, boolean is_sender, [ByRef] sequence<u8> sending_ephemeral_private_key, [ByRef] sequence<u8> receiving_ephemeral_key);
|
||||
DoubleRatchetStateAndEnvelope double_ratchet_encrypt(DoubleRatchetStateAndMessage ratchet_state_and_message);
|
||||
DoubleRatchetStateAndMessage double_ratchet_decrypt(DoubleRatchetStateAndEnvelope ratchet_state_and_envelope);
|
||||
|
||||
TripleRatchetStateAndMetadata new_triple_ratchet([ByRef] sequence<sequence<u8>> peers, [ByRef] sequence<u8> peer_key, [ByRef] sequence<u8> identity_key, [ByRef] sequence<u8> signed_pre_key, u64 threshold, boolean async_dkg_ratchet);
|
||||
TripleRatchetStateAndMetadata triple_ratchet_init_round_1(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
|
||||
TripleRatchetStateAndMetadata triple_ratchet_init_round_2(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
|
||||
TripleRatchetStateAndMetadata triple_ratchet_init_round_3(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
|
||||
TripleRatchetStateAndMetadata triple_ratchet_init_round_4(TripleRatchetStateAndMetadata ratchet_state_and_metadata);
|
||||
TripleRatchetStateAndEnvelope triple_ratchet_encrypt(TripleRatchetStateAndMessage ratchet_state_and_message);
|
||||
TripleRatchetStateAndMessage triple_ratchet_decrypt(TripleRatchetStateAndEnvelope ratchet_state_and_envelope);
|
||||
};
|
||||
|
||||
dictionary DoubleRatchetStateAndEnvelope {
|
||||
string ratchet_state;
|
||||
string envelope;
|
||||
};
|
||||
|
||||
dictionary DoubleRatchetStateAndMessage {
|
||||
string ratchet_state;
|
||||
sequence<u8> message;
|
||||
};
|
||||
|
||||
dictionary TripleRatchetStateAndMetadata {
|
||||
string ratchet_state;
|
||||
record<string, string> metadata;
|
||||
};
|
||||
|
||||
dictionary TripleRatchetStateAndEnvelope {
|
||||
string ratchet_state;
|
||||
string envelope;
|
||||
};
|
||||
|
||||
dictionary TripleRatchetStateAndMessage {
|
||||
string ratchet_state;
|
||||
sequence<u8> message;
|
||||
};
|
622
crates/channel/src/protocols/doubleratchet.rs
Normal file
622
crates/channel/src/protocols/doubleratchet.rs
Normal file
@ -0,0 +1,622 @@
|
||||
use base64::prelude::*;
|
||||
use ed448_goldilocks_plus::elliptic_curve::group::GroupEncoding;
|
||||
use ed448_goldilocks_plus::elliptic_curve::ops::MulByGenerator;
|
||||
use ed448_goldilocks_plus::{subtle, CompressedEdwardsY, EdwardsPoint, Scalar};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use sha2::Sha512;
|
||||
use hkdf::Hkdf;
|
||||
use aes_gcm::{Aes256Gcm, Nonce};
|
||||
use aes_gcm::aead::{Aead, Payload};
|
||||
use std::collections::HashMap;
|
||||
use std::error;
|
||||
use subtle::ConstantTimeEq;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
const DOUBLE_RATCHET_PROTOCOL_VERSION: u16 = 1;
|
||||
const DOUBLE_RATCHET_PROTOCOL: u16 = 1 << 8 + DOUBLE_RATCHET_PROTOCOL_VERSION;
|
||||
|
||||
const CHAIN_KEY: u8 = 0x01;
|
||||
const MESSAGE_KEY: u8 = 0x02;
|
||||
const AEAD_KEY: u8 = 0x03;
|
||||
|
||||
pub struct DoubleRatchetParticipant {
|
||||
sending_ephemeral_private_key: Scalar,
|
||||
receiving_ephemeral_key: EdwardsPoint,
|
||||
root_key: Vec<u8>,
|
||||
sending_chain_key: Vec<u8>,
|
||||
current_sending_header_key: Vec<u8>,
|
||||
current_receiving_header_key: Vec<u8>,
|
||||
next_sending_header_key: Vec<u8>,
|
||||
next_receiving_header_key: Vec<u8>,
|
||||
receiving_chain_key: Vec<u8>,
|
||||
current_sending_chain_length: u32,
|
||||
previous_sending_chain_length: u32,
|
||||
current_receiving_chain_length: u32,
|
||||
previous_receiving_chain_length: u32,
|
||||
skipped_keys_map: HashMap<Vec<u8>, HashMap<u32, Vec<u8>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct DoubleRatchetParticipantJson {
|
||||
pub sending_ephemeral_private_key: String,
|
||||
pub receiving_ephemeral_key: String,
|
||||
pub root_key: String,
|
||||
pub sending_chain_key: String,
|
||||
pub current_sending_header_key: String,
|
||||
pub current_receiving_header_key: String,
|
||||
pub next_sending_header_key: String,
|
||||
pub next_receiving_header_key: String,
|
||||
pub receiving_chain_key: String,
|
||||
pub current_sending_chain_length: u32,
|
||||
pub previous_sending_chain_length: u32,
|
||||
pub current_receiving_chain_length: u32,
|
||||
pub previous_receiving_chain_length: u32,
|
||||
pub skipped_keys_map: HashMap<String, HashMap<u32, String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MessageCiphertext {
|
||||
pub ciphertext: Vec<u8>,
|
||||
pub initialization_vector: Vec<u8>,
|
||||
pub associated_data: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct P2PChannelEnvelope {
|
||||
pub protocol_identifier: u16,
|
||||
pub message_header: MessageCiphertext,
|
||||
pub message_body: MessageCiphertext,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct P2PChannelEnvelopeJson {
|
||||
pub protocol_identifier: u16,
|
||||
pub message_header: MessageCiphertextJson,
|
||||
pub message_body: MessageCiphertextJson,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MessageCiphertextJson {
|
||||
pub ciphertext: String,
|
||||
pub initialization_vector: String,
|
||||
pub associated_data: Option<String>,
|
||||
}
|
||||
|
||||
impl P2PChannelEnvelope {
|
||||
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||
let envelope = P2PChannelEnvelopeJson{
|
||||
protocol_identifier: self.protocol_identifier,
|
||||
message_header: MessageCiphertextJson{
|
||||
ciphertext: BASE64_STANDARD.encode(&self.message_header.ciphertext),
|
||||
initialization_vector: BASE64_STANDARD.encode(&self.message_header.initialization_vector),
|
||||
associated_data: self.message_header.associated_data.clone().map(|a| BASE64_STANDARD.encode(a)),
|
||||
},
|
||||
message_body: MessageCiphertextJson{
|
||||
ciphertext: BASE64_STANDARD.encode(&self.message_body.ciphertext),
|
||||
initialization_vector: BASE64_STANDARD.encode(&self.message_body.initialization_vector),
|
||||
associated_data: self.message_body.associated_data.clone().map(|a| BASE64_STANDARD.encode(a)),
|
||||
},
|
||||
};
|
||||
|
||||
serde_json::to_string(&envelope)
|
||||
}
|
||||
|
||||
pub fn from_json(envelope_json: String) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
|
||||
let envelope: Result<P2PChannelEnvelopeJson, serde_json::Error> = serde_json::from_str(&envelope_json);
|
||||
if envelope.is_err() {
|
||||
return Err(Box::new(envelope.unwrap_err()));
|
||||
}
|
||||
|
||||
let e = envelope.unwrap();
|
||||
let header_ciphertext = BASE64_STANDARD.decode(e.message_header.ciphertext)?;
|
||||
let header_initialization_vector = BASE64_STANDARD.decode(e.message_header.initialization_vector)?;
|
||||
let header_associated_data = e.message_header.associated_data.map(|a| BASE64_STANDARD.decode(a)).transpose()?;
|
||||
let ciphertext = BASE64_STANDARD.decode(e.message_body.ciphertext)?;
|
||||
let initialization_vector = BASE64_STANDARD.decode(e.message_body.initialization_vector)?;
|
||||
let associated_data = e.message_body.associated_data.map(|a| BASE64_STANDARD.decode(a)).transpose()?;
|
||||
|
||||
Ok(P2PChannelEnvelope{
|
||||
protocol_identifier: e.protocol_identifier,
|
||||
message_header: MessageCiphertext{
|
||||
ciphertext: header_ciphertext,
|
||||
initialization_vector: header_initialization_vector,
|
||||
associated_data: header_associated_data,
|
||||
},
|
||||
message_body: MessageCiphertext{
|
||||
ciphertext: ciphertext,
|
||||
initialization_vector: initialization_vector,
|
||||
associated_data: associated_data,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleRatchetParticipant {
|
||||
pub fn new(
|
||||
session_key: &[u8],
|
||||
sending_header_key: &[u8],
|
||||
next_receiving_header_key: &[u8],
|
||||
is_sender: bool,
|
||||
sending_ephemeral_private_key: Scalar,
|
||||
receiving_ephemeral_key: EdwardsPoint,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let mut participant = DoubleRatchetParticipant {
|
||||
sending_ephemeral_private_key,
|
||||
receiving_ephemeral_key,
|
||||
root_key: vec![],
|
||||
sending_chain_key: vec![],
|
||||
current_sending_header_key: sending_header_key.to_vec(),
|
||||
current_receiving_header_key: vec![],
|
||||
next_sending_header_key: vec![],
|
||||
next_receiving_header_key: next_receiving_header_key.to_vec(),
|
||||
receiving_chain_key: vec![],
|
||||
current_sending_chain_length: 0,
|
||||
previous_sending_chain_length: 0,
|
||||
current_receiving_chain_length: 0,
|
||||
previous_receiving_chain_length: 0,
|
||||
skipped_keys_map: HashMap::new(),
|
||||
};
|
||||
|
||||
if is_sender {
|
||||
let dh_output = receiving_ephemeral_key * sending_ephemeral_private_key;
|
||||
let hkdf = Hkdf::<Sha512>::new(Some(session_key), &dh_output.compress().to_bytes());
|
||||
let mut rkck = [0u8; 96];
|
||||
let err = hkdf.expand(b"quilibrium-double-ratchet", &mut rkck);
|
||||
if err.is_err() {
|
||||
return Err("invalid length".into());
|
||||
}
|
||||
|
||||
participant.root_key = rkck[..32].to_vec();
|
||||
participant.sending_chain_key = rkck[32..64].to_vec();
|
||||
participant.next_sending_header_key = rkck[64..].to_vec();
|
||||
} else {
|
||||
participant.root_key = session_key.to_vec();
|
||||
participant.next_sending_header_key = next_receiving_header_key.to_vec();
|
||||
participant.next_receiving_header_key = sending_header_key.to_vec();
|
||||
}
|
||||
|
||||
Ok(participant)
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||
let mut skipped_keys_map = HashMap::<String, HashMap<u32, String>>::new();
|
||||
for (k, v) in &self.skipped_keys_map {
|
||||
let kb = BASE64_STANDARD.encode(k);
|
||||
let mut val = HashMap::<u32, String>::new();
|
||||
for (kk, vv) in v {
|
||||
let vvb = BASE64_STANDARD.encode(vv);
|
||||
val.insert(*kk, vvb);
|
||||
}
|
||||
skipped_keys_map.insert(kb, val);
|
||||
}
|
||||
|
||||
let participant = DoubleRatchetParticipantJson{
|
||||
sending_ephemeral_private_key: BASE64_STANDARD.encode(self.sending_ephemeral_private_key.to_bytes()),
|
||||
receiving_ephemeral_key: BASE64_STANDARD.encode(self.receiving_ephemeral_key.compress().to_bytes()),
|
||||
root_key: BASE64_STANDARD.encode(&self.root_key),
|
||||
sending_chain_key: BASE64_STANDARD.encode(&self.sending_chain_key),
|
||||
current_sending_header_key: BASE64_STANDARD.encode(&self.current_sending_header_key),
|
||||
current_receiving_header_key: BASE64_STANDARD.encode(&self.current_receiving_header_key),
|
||||
next_sending_header_key: BASE64_STANDARD.encode(&self.next_sending_header_key),
|
||||
next_receiving_header_key: BASE64_STANDARD.encode(&self.next_receiving_header_key),
|
||||
receiving_chain_key: BASE64_STANDARD.encode(&self.receiving_chain_key),
|
||||
current_sending_chain_length: self.current_sending_chain_length,
|
||||
previous_sending_chain_length: self.previous_sending_chain_length,
|
||||
current_receiving_chain_length: self.current_receiving_chain_length,
|
||||
previous_receiving_chain_length: self.previous_receiving_chain_length,
|
||||
skipped_keys_map: skipped_keys_map,
|
||||
};
|
||||
|
||||
serde_json::to_string(&participant)
|
||||
}
|
||||
|
||||
pub fn from_json(participant_json: String) -> Result<DoubleRatchetParticipant, Box<dyn std::error::Error>> {
|
||||
let json: Result<DoubleRatchetParticipantJson, serde_json::Error> = serde_json::from_str(&participant_json);
|
||||
match json {
|
||||
Ok(participant) => {
|
||||
let sending_ephemeral_private_key_bytes = BASE64_STANDARD.decode(participant.sending_ephemeral_private_key)?;
|
||||
let receiving_ephemeral_key_bytes = BASE64_STANDARD.decode(participant.receiving_ephemeral_key)?;
|
||||
let root_key = BASE64_STANDARD.decode(participant.root_key)?;
|
||||
let sending_chain_key = BASE64_STANDARD.decode(participant.sending_chain_key)?;
|
||||
let current_sending_header_key = BASE64_STANDARD.decode(participant.current_sending_header_key)?;
|
||||
let current_receiving_header_key = BASE64_STANDARD.decode(participant.current_receiving_header_key)?;
|
||||
let next_sending_header_key = BASE64_STANDARD.decode(participant.next_sending_header_key)?;
|
||||
let next_receiving_header_key = BASE64_STANDARD.decode(participant.next_receiving_header_key)?;
|
||||
let receiving_chain_key = BASE64_STANDARD.decode(participant.receiving_chain_key)?;
|
||||
let current_sending_chain_length = participant.current_sending_chain_length;
|
||||
let previous_sending_chain_length = participant.previous_sending_chain_length;
|
||||
let current_receiving_chain_length = participant.current_receiving_chain_length;
|
||||
let previous_receiving_chain_length = participant.previous_receiving_chain_length;
|
||||
let mut skipped_keys_map = HashMap::<Vec<u8>, HashMap<u32, Vec<u8>>>::new();
|
||||
for (k, v) in participant.skipped_keys_map {
|
||||
let kb = BASE64_STANDARD.decode(k)?;
|
||||
let mut val = HashMap::<u32, Vec<u8>>::new();
|
||||
for (kk, vv) in v {
|
||||
let vvb = BASE64_STANDARD.decode(vv)?;
|
||||
val.insert(kk, vvb);
|
||||
}
|
||||
skipped_keys_map.insert(kb, val);
|
||||
}
|
||||
|
||||
if sending_ephemeral_private_key_bytes.len() != 56 || receiving_ephemeral_key_bytes.len() != 57 {
|
||||
Err("invalid data".into())
|
||||
} else {
|
||||
let mut sending_ephemeral_private_key = [0u8; 56];
|
||||
sending_ephemeral_private_key.copy_from_slice(&sending_ephemeral_private_key_bytes);
|
||||
|
||||
let mut receiving_ephemeral_key = [0u8; 57];
|
||||
receiving_ephemeral_key.copy_from_slice(&receiving_ephemeral_key_bytes);
|
||||
|
||||
let receiving_ephemeral_ct = EdwardsPoint::from_bytes(&receiving_ephemeral_key.into());
|
||||
if receiving_ephemeral_ct.is_none().into() {
|
||||
Err("invalid data".into())
|
||||
} else {
|
||||
Ok(DoubleRatchetParticipant{
|
||||
sending_ephemeral_private_key: Scalar::from_bytes(&sending_ephemeral_private_key),
|
||||
receiving_ephemeral_key: receiving_ephemeral_ct.unwrap(),
|
||||
root_key: root_key,
|
||||
sending_chain_key: sending_chain_key,
|
||||
current_sending_header_key: current_sending_header_key,
|
||||
current_receiving_header_key: current_receiving_header_key,
|
||||
next_sending_header_key: next_sending_header_key,
|
||||
next_receiving_header_key: next_receiving_header_key,
|
||||
receiving_chain_key: receiving_chain_key,
|
||||
current_sending_chain_length: current_sending_chain_length,
|
||||
previous_sending_chain_length: previous_sending_chain_length,
|
||||
current_receiving_chain_length: current_receiving_chain_length,
|
||||
previous_receiving_chain_length: previous_receiving_chain_length,
|
||||
skipped_keys_map: skipped_keys_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
Err(Box::new(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ratchet_encrypt(&mut self, message: &[u8]) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
|
||||
let mut envelope = P2PChannelEnvelope {
|
||||
protocol_identifier: DOUBLE_RATCHET_PROTOCOL,
|
||||
message_header: MessageCiphertext::default(),
|
||||
message_body: MessageCiphertext::default(),
|
||||
};
|
||||
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.sending_chain_key);
|
||||
self.sending_chain_key = new_chain_key;
|
||||
|
||||
let header = self.encode_header();
|
||||
envelope.message_header = self.encrypt(&header, &self.current_sending_header_key, None)?;
|
||||
|
||||
envelope.message_body = self.encrypt(
|
||||
message,
|
||||
&message_key,
|
||||
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
|
||||
)?;
|
||||
|
||||
self.current_sending_chain_length += 1;
|
||||
|
||||
Ok(envelope)
|
||||
}
|
||||
|
||||
pub fn ratchet_decrypt(&mut self, envelope: &P2PChannelEnvelope) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
if let Some(plaintext) = self.try_skipped_message_keys(envelope)? {
|
||||
return Ok(plaintext);
|
||||
}
|
||||
|
||||
let (header, should_ratchet) = self.decrypt_header(&envelope.message_header, &self.current_receiving_header_key)?;
|
||||
|
||||
let (receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length) =
|
||||
self.decode_header(&header)?;
|
||||
|
||||
if should_ratchet {
|
||||
self.skip_message_keys(previous_receiving_chain_length)?;
|
||||
self.ratchet_ephemeral_keys(&receiving_ephemeral_key)?;
|
||||
}
|
||||
|
||||
self.skip_message_keys(current_receiving_chain_length)?;
|
||||
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.receiving_chain_key);
|
||||
|
||||
let plaintext = self.decrypt(
|
||||
&envelope.message_body,
|
||||
&message_key,
|
||||
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
|
||||
)?;
|
||||
|
||||
self.receiving_chain_key = new_chain_key;
|
||||
self.current_receiving_chain_length += 1;
|
||||
|
||||
Ok(plaintext)
|
||||
}
|
||||
|
||||
fn ratchet_ephemeral_keys(&mut self, new_receiving_ephemeral_key: &EdwardsPoint) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.previous_sending_chain_length = self.current_sending_chain_length;
|
||||
self.current_sending_chain_length = 0;
|
||||
self.current_receiving_chain_length = 0;
|
||||
self.current_sending_header_key = self.next_sending_header_key.clone();
|
||||
self.current_receiving_header_key = self.next_receiving_header_key.clone();
|
||||
self.receiving_ephemeral_key = *new_receiving_ephemeral_key;
|
||||
|
||||
// Perform DH and KDF to get new root key and receiving chain key
|
||||
let dh_output = new_receiving_ephemeral_key * self.sending_ephemeral_private_key;
|
||||
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output.compress().to_bytes());
|
||||
let mut rkck = [0u8; 96];
|
||||
hkdf.expand(b"quilibrium-double-ratchet", &mut rkck);
|
||||
|
||||
self.root_key = rkck[..32].to_vec();
|
||||
self.receiving_chain_key = rkck[32..64].to_vec();
|
||||
self.next_receiving_header_key = rkck[64..].to_vec();
|
||||
|
||||
// Generate new sending ephemeral key
|
||||
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
|
||||
|
||||
// Perform DH and KDF to get new root key and sending chain key
|
||||
let dh_output = new_receiving_ephemeral_key * self.sending_ephemeral_private_key;
|
||||
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output.compress().to_bytes());
|
||||
let mut rkck2 = [0u8; 96];
|
||||
hkdf.expand(b"quilibrium-double-ratchet", &mut rkck2);
|
||||
|
||||
self.root_key = rkck2[..32].to_vec();
|
||||
self.sending_chain_key = rkck2[32..64].to_vec();
|
||||
self.next_sending_header_key = rkck2[64..].to_vec();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_skipped_message_keys(&self, envelope: &P2PChannelEnvelope) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
|
||||
for (receiving_header_key, skipped_keys) in &self.skipped_keys_map {
|
||||
if let Ok((header, _)) = self.decrypt_header(&envelope.message_header, receiving_header_key) {
|
||||
let (_, _, current) = self.decode_header(&header)?;
|
||||
if let Some(key_pair) = skipped_keys.get(¤t) {
|
||||
let message_key = &key_pair[..32];
|
||||
let aead_key = &key_pair[32..];
|
||||
return self.decrypt(
|
||||
&envelope.message_body,
|
||||
message_key,
|
||||
Some(&[aead_key, &envelope.message_header.ciphertext[..]].concat()),
|
||||
).map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn skip_message_keys(&mut self, until: u32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if self.current_receiving_chain_length + 100 < until {
|
||||
return Err("Skip limit exceeded".into());
|
||||
}
|
||||
|
||||
if !self.receiving_chain_key.is_empty() {
|
||||
while self.current_receiving_chain_length < until {
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.receiving_chain_key);
|
||||
self.skipped_keys_map
|
||||
.entry(self.current_receiving_header_key.clone())
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(self.current_receiving_chain_length, [&message_key[..], &aead_key[..]].concat());
|
||||
self.receiving_chain_key = new_chain_key;
|
||||
self.current_receiving_chain_length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encode_header(&self) -> Vec<u8> {
|
||||
let mut header = Vec::new();
|
||||
header.extend_from_slice(&EdwardsPoint::mul_by_generator(&self.sending_ephemeral_private_key).compress().to_bytes());
|
||||
header.extend_from_slice(&self.previous_sending_chain_length.to_be_bytes());
|
||||
header.extend_from_slice(&self.current_sending_chain_length.to_be_bytes());
|
||||
header
|
||||
}
|
||||
|
||||
fn decrypt_header(&self, ciphertext: &MessageCiphertext, receiving_header_key: &[u8])
|
||||
-> Result<(Vec<u8>, bool), Box<dyn std::error::Error>> {
|
||||
match self.decrypt(ciphertext, receiving_header_key, None) {
|
||||
Ok(header) => Ok((header, false)),
|
||||
Err(_) if receiving_header_key.ct_eq(self.current_receiving_header_key.as_slice()).into() => {
|
||||
self.decrypt(ciphertext, &self.next_receiving_header_key, None)
|
||||
.map(|header| (header, true))
|
||||
},
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_header(&self, header: &[u8]) -> Result<(EdwardsPoint, u32, u32), Box<dyn std::error::Error>> {
|
||||
if header.len() < 57 { // 57 bytes for EdwardsPoint + 8 bytes for two u32
|
||||
return Err("Malformed header".into());
|
||||
}
|
||||
|
||||
let receiving_ephemeral_key = CompressedEdwardsY(header[..57].try_into().unwrap()).decompress();
|
||||
if receiving_ephemeral_key.is_none().into() {
|
||||
return Err("Malformed point".into());
|
||||
}
|
||||
let previous_receiving_chain_length = u32::from_be_bytes(header[57..61].try_into()?);
|
||||
let current_receiving_chain_length = u32::from_be_bytes(header[61..65].try_into()?);
|
||||
|
||||
Ok((receiving_ephemeral_key.unwrap(), previous_receiving_chain_length, current_receiving_chain_length))
|
||||
}
|
||||
|
||||
fn encrypt(&self, plaintext: &[u8], key: &[u8], associated_data: Option<&[u8]>)
|
||||
-> Result<MessageCiphertext, Box<dyn std::error::Error>> {
|
||||
use aes_gcm::KeyInit;
|
||||
let mut iv = [0u8; 12];
|
||||
OsRng.fill_bytes(&mut iv);
|
||||
|
||||
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
|
||||
let nonce = Nonce::from_slice(&iv);
|
||||
|
||||
let mut associated_data = associated_data.unwrap_or(&[]);
|
||||
let mut aad = [0u8; 32];
|
||||
if associated_data.len() == 0 {
|
||||
OsRng.fill_bytes(&mut aad);
|
||||
associated_data = &aad
|
||||
}
|
||||
|
||||
let ciphertext = cipher.encrypt(nonce, Payload{
|
||||
msg: plaintext,
|
||||
aad: associated_data,
|
||||
}).map_err(|e| format!("Encryption failed: {}", e))?;
|
||||
|
||||
Ok(MessageCiphertext {
|
||||
ciphertext,
|
||||
initialization_vector: iv.to_vec(),
|
||||
associated_data: Some(associated_data.to_vec()),
|
||||
})
|
||||
}
|
||||
|
||||
fn decrypt(&self, ciphertext: &MessageCiphertext, key: &[u8], associated_data: Option<&[u8]>)
|
||||
-> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
use aes_gcm::KeyInit;
|
||||
if key.len() != 32 {
|
||||
return Err(format!("Invalid key length").into());
|
||||
}
|
||||
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
|
||||
let nonce = Nonce::from_slice(&ciphertext.initialization_vector);
|
||||
|
||||
let associated_data = associated_data.unwrap_or_else(|| ciphertext.associated_data.as_ref().unwrap());
|
||||
|
||||
cipher.decrypt(nonce, Payload{
|
||||
msg: ciphertext.ciphertext.as_slice(),
|
||||
aad: associated_data,
|
||||
}).map_err(|e| format!("Decryption failed: {}", e).into())
|
||||
}
|
||||
|
||||
pub fn rotate_sending_key(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
|
||||
self.ratchet_ephemeral_keys(&self.receiving_ephemeral_key.clone())
|
||||
}
|
||||
|
||||
pub fn get_public_key(&self) -> EdwardsPoint {
|
||||
EdwardsPoint::mul_by_generator(&self.sending_ephemeral_private_key)
|
||||
}
|
||||
}
|
||||
|
||||
fn ratchet_keys(input_key: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
||||
use hmac::Mac;
|
||||
let mut aead_key = [0u8; 64];
|
||||
let mut message_key = [0u8; 64];
|
||||
let mut chain_key = [0u8; 64];
|
||||
|
||||
let mut hmac_aead = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
|
||||
hmac_aead.update(&[AEAD_KEY]);
|
||||
aead_key.copy_from_slice(&hmac_aead.finalize().into_bytes());
|
||||
|
||||
let mut hmac_message = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
|
||||
hmac_message.update(&[MESSAGE_KEY]);
|
||||
message_key.copy_from_slice(&hmac_message.finalize().into_bytes());
|
||||
|
||||
let mut hmac_chain = hmac::Hmac::<Sha512>::new_from_slice(input_key).unwrap();
|
||||
hmac_chain.update(&[CHAIN_KEY]);
|
||||
chain_key.copy_from_slice(&hmac_chain.finalize().into_bytes());
|
||||
|
||||
(chain_key[..32].to_vec(), message_key[..32].to_vec(), aead_key[..32].to_vec())
|
||||
}
|
||||
|
||||
// Implementation for MessageCiphertext
|
||||
impl Default for MessageCiphertext {
|
||||
fn default() -> Self {
|
||||
MessageCiphertext {
|
||||
ciphertext: Vec::new(),
|
||||
initialization_vector: Vec::new(),
|
||||
associated_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation for P2PChannelEnvelope
|
||||
impl P2PChannelEnvelope {
|
||||
pub fn new(protocol_identifier: u16) -> Self {
|
||||
P2PChannelEnvelope {
|
||||
protocol_identifier,
|
||||
message_header: MessageCiphertext::default(),
|
||||
message_body: MessageCiphertext::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ed448_goldilocks_plus::Scalar;
|
||||
|
||||
#[test]
|
||||
fn test_double_ratchet_communication() {
|
||||
let session_key = [0u8; 32];
|
||||
let sending_header_key = [1u8; 32];
|
||||
let next_receiving_header_key = [2u8; 32];
|
||||
|
||||
let alice_ephemeral = Scalar::random(&mut OsRng);
|
||||
let bob_ephemeral = Scalar::random(&mut OsRng);
|
||||
|
||||
let alice_public = EdwardsPoint::mul_by_generator(&alice_ephemeral);
|
||||
let bob_public = EdwardsPoint::mul_by_generator(&bob_ephemeral);
|
||||
|
||||
let mut alice = DoubleRatchetParticipant::new(
|
||||
&session_key,
|
||||
&sending_header_key,
|
||||
&next_receiving_header_key,
|
||||
true,
|
||||
alice_ephemeral,
|
||||
bob_public,
|
||||
).unwrap();
|
||||
|
||||
let mut bob = DoubleRatchetParticipant::new(
|
||||
&session_key,
|
||||
&sending_header_key,
|
||||
&next_receiving_header_key,
|
||||
false,
|
||||
bob_ephemeral,
|
||||
alice_public,
|
||||
).unwrap();
|
||||
|
||||
// Test message exchange
|
||||
let message = b"Hello, Bob!";
|
||||
let envelope = alice.ratchet_encrypt(message).unwrap();
|
||||
|
||||
let decrypted = bob.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(message, decrypted.as_slice());
|
||||
|
||||
let response = b"Hello, Alice!";
|
||||
let envelope = bob.ratchet_encrypt(response).unwrap();
|
||||
let delayed = alice.ratchet_encrypt(b"force another step").unwrap();
|
||||
let decrypted = alice.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(response, decrypted.as_slice());
|
||||
|
||||
// Test multiple messages
|
||||
for _ in 0..5 {
|
||||
let message = b"Secure communication test";
|
||||
let envelope = alice.ratchet_encrypt(message).unwrap();
|
||||
let decrypted = bob.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(message, decrypted.as_slice());
|
||||
|
||||
let response = b"Acknowledged";
|
||||
let envelope = bob.ratchet_encrypt(response).unwrap();
|
||||
let decrypted = alice.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(response, decrypted.as_slice());
|
||||
}
|
||||
|
||||
let alice_json = alice.to_json().unwrap();
|
||||
let bob_json = bob.to_json().unwrap();
|
||||
|
||||
let mut new_alice = DoubleRatchetParticipant::from_json(alice_json).unwrap();
|
||||
let mut new_bob = DoubleRatchetParticipant::from_json(bob_json).unwrap();
|
||||
|
||||
// Test multiple messages
|
||||
for _ in 0..5 {
|
||||
let message = b"Secure communication test";
|
||||
let envelope = new_alice.ratchet_encrypt(message).unwrap();
|
||||
let decrypted = new_bob.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(message, decrypted.as_slice());
|
||||
|
||||
let response = b"Acknowledged";
|
||||
let envelope = new_bob.ratchet_encrypt(response).unwrap();
|
||||
let decrypted = new_alice.ratchet_decrypt(&envelope).unwrap();
|
||||
assert_eq!(response, decrypted.as_slice());
|
||||
}
|
||||
let decrypted = new_bob.ratchet_decrypt(&delayed).unwrap();
|
||||
assert_eq!(b"force another step", decrypted.as_slice());
|
||||
}
|
||||
}
|
485
crates/channel/src/protocols/feldman.rs
Normal file
485
crates/channel/src/protocols/feldman.rs
Normal file
@ -0,0 +1,485 @@
|
||||
use base64::prelude::*;
|
||||
use std::{collections::HashMap, io::Read};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha512};
|
||||
use ed448_goldilocks_plus::{elliptic_curve::{group::GroupEncoding, Field, Group}, subtle::ConstantTimeEq, EdwardsPoint, Scalar};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum FeldmanError {
|
||||
#[error("Wrong round for Feldman operation")]
|
||||
WrongRound,
|
||||
#[error("Invalid data: {0}")]
|
||||
InvalidData(String),
|
||||
#[error("Crypto error: {0}")]
|
||||
CryptoError(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum FeldmanRound {
|
||||
Uninitialized,
|
||||
Initialized,
|
||||
Committed,
|
||||
Revealed,
|
||||
Reconstructed,
|
||||
}
|
||||
|
||||
pub struct Feldman {
|
||||
threshold: usize,
|
||||
total: usize,
|
||||
id: usize,
|
||||
frags_for_counterparties: HashMap<usize, Vec<u8>>,
|
||||
frags_from_counterparties: HashMap<usize, Scalar>,
|
||||
zkpok: Option<Scalar>,
|
||||
secret: Scalar,
|
||||
scalar: Option<Scalar>,
|
||||
generator: EdwardsPoint,
|
||||
public_key: EdwardsPoint,
|
||||
point: EdwardsPoint,
|
||||
random_commitment_point: Option<EdwardsPoint>,
|
||||
round: FeldmanRound,
|
||||
zkcommits_from_counterparties: HashMap<usize, Vec<u8>>,
|
||||
points_from_counterparties: HashMap<usize, EdwardsPoint>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FeldmanJson {
|
||||
threshold: usize,
|
||||
total: usize,
|
||||
id: usize,
|
||||
frags_for_counterparties: HashMap<usize, String>,
|
||||
frags_from_counterparties: HashMap<usize, String>,
|
||||
zkpok: Option<String>,
|
||||
secret: String,
|
||||
scalar: Option<String>,
|
||||
generator: String,
|
||||
public_key: String,
|
||||
point: String,
|
||||
random_commitment_point: Option<String>,
|
||||
round: usize,
|
||||
zkcommits_from_counterparties: HashMap<usize, String>,
|
||||
points_from_counterparties: HashMap<usize, String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct FeldmanReveal {
|
||||
point: Vec<u8>,
|
||||
random_commitment_point: Vec<u8>,
|
||||
zk_pok: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn vec_to_array<const N: usize>(v: Vec<u8>) -> Result<[u8; N], Box<dyn std::error::Error>> {
|
||||
if v.len() != N {
|
||||
return Err(format!("Invalid length: expected {}, got {}", N, v.len()).into());
|
||||
}
|
||||
|
||||
let mut arr: [u8; N] = [0u8; N];
|
||||
arr.copy_from_slice(&v);
|
||||
Ok(arr)
|
||||
}
|
||||
|
||||
impl Feldman {
|
||||
pub fn new(
|
||||
threshold: usize,
|
||||
total: usize,
|
||||
id: usize,
|
||||
secret: Scalar,
|
||||
generator: EdwardsPoint,
|
||||
) -> Self {
|
||||
Feldman {
|
||||
threshold,
|
||||
total,
|
||||
id,
|
||||
frags_for_counterparties: HashMap::new(),
|
||||
frags_from_counterparties: HashMap::new(),
|
||||
zkpok: None,
|
||||
secret,
|
||||
scalar: None,
|
||||
generator,
|
||||
public_key: EdwardsPoint::generator(),
|
||||
point: EdwardsPoint::generator(),
|
||||
random_commitment_point: None,
|
||||
round: FeldmanRound::Uninitialized,
|
||||
zkcommits_from_counterparties: HashMap::new(),
|
||||
points_from_counterparties: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||
let feldman_json = FeldmanJson {
|
||||
threshold: self.threshold,
|
||||
total: self.total,
|
||||
id: self.id,
|
||||
frags_for_counterparties: self.frags_for_counterparties.iter()
|
||||
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v)))
|
||||
.collect(),
|
||||
frags_from_counterparties: self.frags_from_counterparties.iter()
|
||||
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v.to_bytes())))
|
||||
.collect(),
|
||||
zkpok: self.zkpok.as_ref().map(|s| BASE64_STANDARD.encode(s.to_bytes())),
|
||||
secret: BASE64_STANDARD.encode(self.secret.to_bytes()),
|
||||
scalar: self.scalar.as_ref().map(|s| BASE64_STANDARD.encode(s.to_bytes())),
|
||||
generator: BASE64_STANDARD.encode(self.generator.compress().to_bytes()),
|
||||
public_key: BASE64_STANDARD.encode(self.public_key.compress().to_bytes()),
|
||||
point: BASE64_STANDARD.encode(self.point.compress().to_bytes()),
|
||||
random_commitment_point: self.random_commitment_point.as_ref()
|
||||
.map(|p| BASE64_STANDARD.encode(p.compress().to_bytes())),
|
||||
round: self.round as usize,
|
||||
zkcommits_from_counterparties: self.zkcommits_from_counterparties.iter()
|
||||
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v)))
|
||||
.collect(),
|
||||
points_from_counterparties: self.points_from_counterparties.iter()
|
||||
.map(|(&k, v)| (k, BASE64_STANDARD.encode(v.compress().to_bytes())))
|
||||
.collect(),
|
||||
};
|
||||
|
||||
serde_json::to_string(&feldman_json)
|
||||
}
|
||||
|
||||
pub fn from_json(json: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let feldman_json: FeldmanJson = serde_json::from_str(json)?;
|
||||
|
||||
let frags_for_counterparties = feldman_json.frags_for_counterparties.into_iter()
|
||||
.map(|(k, v)| Ok((k, BASE64_STANDARD.decode(v)?)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let frags_from_counterparties = feldman_json.frags_from_counterparties.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let bytes = BASE64_STANDARD.decode(v)?;
|
||||
Ok((k, Scalar::from_bytes(&vec_to_array::<56>(bytes)?)))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let mut zkpok: Option<Scalar> = None;
|
||||
if feldman_json.zkpok.is_some() {
|
||||
let bytes = BASE64_STANDARD.decode(feldman_json.zkpok.unwrap())?;
|
||||
zkpok = Some(Scalar::from_bytes(&vec_to_array::<56>(bytes)?));
|
||||
}
|
||||
|
||||
let secret_bytes = BASE64_STANDARD.decode(feldman_json.secret)?;
|
||||
let secret = Scalar::from_bytes(&vec_to_array::<56>(secret_bytes)?);
|
||||
|
||||
let mut scalar: Option<Scalar> = None;
|
||||
if feldman_json.scalar.is_some() {
|
||||
let bytes = BASE64_STANDARD.decode(feldman_json.scalar.unwrap())?;
|
||||
scalar = Some(Scalar::from_bytes(&vec_to_array::<56>(bytes)?));
|
||||
}
|
||||
|
||||
let generator_bytes = BASE64_STANDARD.decode(feldman_json.generator)?;
|
||||
let generator = EdwardsPoint::from_bytes(&vec_to_array::<57>(generator_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
|
||||
|
||||
let public_key_bytes = BASE64_STANDARD.decode(feldman_json.public_key)?;
|
||||
let public_key = EdwardsPoint::from_bytes(&vec_to_array::<57>(public_key_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
|
||||
|
||||
let point_bytes = BASE64_STANDARD.decode(feldman_json.point)?;
|
||||
let point = EdwardsPoint::from_bytes(&vec_to_array::<57>(point_bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?;
|
||||
|
||||
let mut random_commitment_point: Option<EdwardsPoint> = None;
|
||||
if feldman_json.random_commitment_point.is_some() {
|
||||
let bytes = BASE64_STANDARD.decode(feldman_json.random_commitment_point.unwrap())?;
|
||||
random_commitment_point = Some(EdwardsPoint::from_bytes(&vec_to_array::<57>(bytes)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?);
|
||||
}
|
||||
|
||||
let zkcommits_from_counterparties = feldman_json.zkcommits_from_counterparties.into_iter()
|
||||
.map(|(k, v)| Ok((k, BASE64_STANDARD.decode(v)?)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let points_from_counterparties = feldman_json.points_from_counterparties.into_iter()
|
||||
.map(|(k, v)| {
|
||||
Ok((k, EdwardsPoint::from_bytes(&vec_to_array::<57>(BASE64_STANDARD.decode(v)?)?.into()).into_option().ok_or_else(|| FeldmanError::InvalidData("invalid data".into()))?))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
Ok(Feldman {
|
||||
threshold: feldman_json.threshold,
|
||||
total: feldman_json.total,
|
||||
id: feldman_json.id,
|
||||
frags_for_counterparties,
|
||||
frags_from_counterparties,
|
||||
zkpok,
|
||||
secret,
|
||||
scalar,
|
||||
generator,
|
||||
public_key,
|
||||
point,
|
||||
random_commitment_point,
|
||||
round: match feldman_json.round {
|
||||
0 => FeldmanRound::Uninitialized,
|
||||
1 => FeldmanRound::Initialized,
|
||||
2 => FeldmanRound::Committed,
|
||||
3 => FeldmanRound::Revealed,
|
||||
4 => FeldmanRound::Reconstructed,
|
||||
_ => FeldmanRound::Uninitialized,
|
||||
},
|
||||
zkcommits_from_counterparties,
|
||||
points_from_counterparties,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_id(&mut self, id: usize) {
|
||||
self.id = id;
|
||||
}
|
||||
|
||||
pub fn sample_polynomial<R: RngCore + CryptoRng>(&mut self, rng: &mut R) -> Result<(), FeldmanError> {
|
||||
if self.round != FeldmanRound::Uninitialized {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
|
||||
let mut coeffs = vec![self.secret];
|
||||
|
||||
for _ in 1..self.threshold {
|
||||
coeffs.push(Scalar::random(rng));
|
||||
}
|
||||
|
||||
for i in 1..=self.total {
|
||||
let mut result = coeffs[0];
|
||||
let x = Scalar::from(i as u32);
|
||||
|
||||
for j in 1..self.threshold {
|
||||
let term = coeffs[j] * Scalar::from(i.pow(j as u32) as u32);
|
||||
result += term;
|
||||
}
|
||||
|
||||
if i == self.id {
|
||||
self.scalar = Some(result);
|
||||
} else {
|
||||
self.frags_for_counterparties.insert(i, result.to_bytes().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
self.round = FeldmanRound::Initialized;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn scalar(&self) -> Option<&Scalar> {
|
||||
self.scalar.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_poly_frags(&self) -> Result<&HashMap<usize, Vec<u8>>, FeldmanError> {
|
||||
if self.round != FeldmanRound::Initialized {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
Ok(&self.frags_for_counterparties)
|
||||
}
|
||||
|
||||
pub fn set_poly_frag_for_party(&mut self, id: usize, frag: &[u8]) -> Result<Option<Vec<u8>>, FeldmanError> {
|
||||
if self.round != FeldmanRound::Initialized {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
|
||||
let scalar = Scalar::from_bytes(frag.try_into().unwrap());
|
||||
self.frags_from_counterparties.insert(id, scalar);
|
||||
|
||||
if self.frags_from_counterparties.len() == self.total - 1 {
|
||||
let mut combined_scalar = self.scalar.unwrap_or_else(|| Scalar::ZERO);
|
||||
for scalar in self.frags_from_counterparties.values() {
|
||||
combined_scalar += *scalar;
|
||||
}
|
||||
self.scalar = Some(combined_scalar);
|
||||
|
||||
self.point = self.generator * combined_scalar;
|
||||
|
||||
let rand_commitment = Scalar::random(&mut rand::thread_rng());
|
||||
self.random_commitment_point = Some(self.generator * rand_commitment);
|
||||
|
||||
let random_commitment_point_bytes = self.random_commitment_point.unwrap().compress().to_bytes();
|
||||
let public_point_bytes = self.point.compress().to_bytes();
|
||||
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(&public_point_bytes);
|
||||
hasher.update(&random_commitment_point_bytes);
|
||||
let challenge = hasher.finalize();
|
||||
|
||||
let challenge_scalar = Scalar::from_bytes(challenge[..56].try_into().unwrap());
|
||||
|
||||
self.zkpok = Some(combined_scalar * challenge_scalar + rand_commitment);
|
||||
|
||||
let zkpok_bytes = self.zkpok.unwrap().to_bytes();
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(&random_commitment_point_bytes);
|
||||
hasher.update(&zkpok_bytes);
|
||||
let zkcommit = hasher.finalize();
|
||||
|
||||
self.round = FeldmanRound::Committed;
|
||||
return Ok(Some(zkcommit[..56].to_vec()));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn receive_commitments(&mut self, id: usize, zkcommit: &[u8]) -> Result<Option<FeldmanReveal>, FeldmanError> {
|
||||
if self.round != FeldmanRound::Committed {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
|
||||
self.zkcommits_from_counterparties.insert(id, zkcommit.to_vec());
|
||||
|
||||
if self.zkcommits_from_counterparties.len() == self.total - 1 {
|
||||
let public_point_bytes = self.point.compress().to_bytes();
|
||||
let random_commitment_point_bytes = self.random_commitment_point.unwrap().compress().to_bytes();
|
||||
self.round = FeldmanRound::Revealed;
|
||||
let zkpok_bytes = self.zkpok.unwrap().to_bytes();
|
||||
|
||||
return Ok(Some(FeldmanReveal {
|
||||
point: public_point_bytes.to_vec(),
|
||||
random_commitment_point: random_commitment_point_bytes.to_vec(),
|
||||
zk_pok: zkpok_bytes.to_vec(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn recombine(&mut self, id: usize, reveal: &FeldmanReveal) -> Result<bool, FeldmanError> {
|
||||
if self.round != FeldmanRound::Revealed {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
|
||||
let counterparty_point = EdwardsPoint::from_bytes(reveal.point.as_slice().into()).unwrap();
|
||||
|
||||
if counterparty_point.eq(&EdwardsPoint::generator()).into() || counterparty_point == self.generator {
|
||||
return Err(FeldmanError::InvalidData("Counterparty sent generator".into()));
|
||||
}
|
||||
|
||||
let counterparty_random_commitment_point = EdwardsPoint::from_bytes(reveal.random_commitment_point.as_slice().into()).unwrap();
|
||||
|
||||
if counterparty_random_commitment_point.eq(&EdwardsPoint::generator()).into() || counterparty_random_commitment_point == self.generator {
|
||||
return Err(FeldmanError::InvalidData("Counterparty sent generator".into()));
|
||||
}
|
||||
|
||||
let counterparty_zkpok = Scalar::from_bytes(reveal.zk_pok.as_slice().try_into().unwrap());
|
||||
|
||||
let counterparty_zkcommit = self.zkcommits_from_counterparties.get(&id)
|
||||
.ok_or_else(|| FeldmanError::InvalidData("Missing ZK commit for counterparty".into()))?;
|
||||
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(&reveal.point);
|
||||
hasher.update(&reveal.random_commitment_point);
|
||||
let challenge = hasher.finalize();
|
||||
|
||||
let challenge_scalar = Scalar::from_bytes(challenge[..56].try_into().unwrap());
|
||||
|
||||
let proof = self.generator * counterparty_zkpok;
|
||||
let expected_proof = counterparty_random_commitment_point + (counterparty_point * challenge_scalar);
|
||||
|
||||
if proof != expected_proof {
|
||||
return Err(FeldmanError::InvalidData(format!("Invalid proof from {}", id)));
|
||||
}
|
||||
|
||||
let mut hasher = Sha512::new();
|
||||
hasher.update(&reveal.random_commitment_point);
|
||||
hasher.update(&reveal.zk_pok);
|
||||
let verifier = hasher.finalize();
|
||||
|
||||
if &verifier[..56] != counterparty_zkcommit {
|
||||
return Err(FeldmanError::InvalidData(format!("{} changed zkpok after commit", id)));
|
||||
}
|
||||
|
||||
self.points_from_counterparties.insert(id, counterparty_point);
|
||||
|
||||
if self.points_from_counterparties.len() == self.total - 1 {
|
||||
self.points_from_counterparties.insert(self.id, self.point);
|
||||
|
||||
for i in 1..=self.total - self.threshold + 1 {
|
||||
let mut reconstructed_sum = EdwardsPoint::generator();
|
||||
|
||||
for j in i..self.threshold + i {
|
||||
let mut num = Scalar::ONE;
|
||||
let mut den = Scalar::ONE;
|
||||
|
||||
for k in i..self.threshold + i {
|
||||
if j != k {
|
||||
let j_scalar = Scalar::from(j as u32);
|
||||
let k_scalar = Scalar::from(k as u32);
|
||||
|
||||
num *= k_scalar;
|
||||
den *= k_scalar - j_scalar;
|
||||
}
|
||||
}
|
||||
|
||||
let den_inv = den.invert();
|
||||
let reconstructed_fragment = self.points_from_counterparties[&j] * (num * den_inv);
|
||||
reconstructed_sum += reconstructed_fragment;
|
||||
}
|
||||
|
||||
if self.public_key == EdwardsPoint::generator() || self.public_key == self.generator {
|
||||
self.public_key = reconstructed_sum;
|
||||
} else if self.public_key != reconstructed_sum {
|
||||
return Err(FeldmanError::InvalidData("Recombination mismatch".into()));
|
||||
}
|
||||
}
|
||||
self.round = FeldmanRound::Reconstructed;
|
||||
}
|
||||
|
||||
Ok(self.round == FeldmanRound::Reconstructed)
|
||||
}
|
||||
|
||||
pub fn mul_share(&self, pubkey: &[u8]) -> Result<Vec<u8>, FeldmanError> {
|
||||
if self.scalar.is_none() {
|
||||
return Err(FeldmanError::WrongRound);
|
||||
}
|
||||
|
||||
let point = EdwardsPoint::from_bytes(pubkey.into());
|
||||
if point.is_none().into() {
|
||||
return Err(FeldmanError::InvalidData("invalid pubkey".to_string()));
|
||||
}
|
||||
|
||||
let result = self.scalar.unwrap() * point.unwrap();
|
||||
if result.is_identity().into() {
|
||||
return Err(FeldmanError::InvalidData("invalid pubkey".to_string()));
|
||||
}
|
||||
|
||||
return Ok(result.compress().to_bytes().to_vec());
|
||||
}
|
||||
|
||||
pub fn combine_mul_share(&mut self, shares: Vec<&[u8]>, ids: &[usize]) -> Result<Vec<u8>, FeldmanError> {
|
||||
if shares.len() != ids.len() {
|
||||
return Err(FeldmanError::InvalidData("mismatch of shares and ids len".to_string()));
|
||||
}
|
||||
|
||||
let mut points = HashMap::<usize, EdwardsPoint>::new();
|
||||
for (i, share) in shares.iter().enumerate() {
|
||||
let point = EdwardsPoint::from_bytes((*share).into());
|
||||
if point.is_none().into() {
|
||||
return Err(FeldmanError::InvalidData(format!("invalid pubkey for {}", ids[i]).to_string()));
|
||||
}
|
||||
|
||||
points.insert(ids[i], point.unwrap());
|
||||
}
|
||||
|
||||
let mut reconstructed_sum = EdwardsPoint::generator();
|
||||
|
||||
for j in ids {
|
||||
let mut num = Scalar::ONE;
|
||||
let mut den = Scalar::ONE;
|
||||
|
||||
for k in ids {
|
||||
if j != k {
|
||||
let j_scalar = Scalar::from(*j as u32);
|
||||
let k_scalar = Scalar::from(*k as u32);
|
||||
|
||||
num *= k_scalar;
|
||||
den *= k_scalar - j_scalar;
|
||||
}
|
||||
}
|
||||
|
||||
let den_inv = den.invert();
|
||||
let reconstructed_fragment = points[&j] * (num * den_inv);
|
||||
reconstructed_sum += reconstructed_fragment;
|
||||
}
|
||||
|
||||
self.public_key = reconstructed_sum;
|
||||
|
||||
return Ok(reconstructed_sum.compress().to_bytes().to_vec());
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> &EdwardsPoint {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
pub fn public_key_bytes(&self) -> Vec<u8> {
|
||||
self.public_key.to_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
4
crates/channel/src/protocols/mod.rs
Normal file
4
crates/channel/src/protocols/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub(crate) mod doubleratchet;
|
||||
pub(crate) mod tripleratchet;
|
||||
pub(crate) mod feldman;
|
||||
pub(crate) mod x3dh;
|
883
crates/channel/src/protocols/tripleratchet.rs
Normal file
883
crates/channel/src/protocols/tripleratchet.rs
Normal file
@ -0,0 +1,883 @@
|
||||
use base64::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use ed448_goldilocks_plus::elliptic_curve::group::GroupEncoding;
|
||||
use ed448_goldilocks_plus::elliptic_curve::Group;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use sha2::{Sha512, Digest};
|
||||
use hkdf::Hkdf;
|
||||
use aes_gcm::{Aes256Gcm, Nonce};
|
||||
use aes_gcm::aead::{Aead, Payload};
|
||||
use ed448_goldilocks_plus::{subtle, EdwardsPoint, Scalar};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use thiserror::Error;
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use super::doubleratchet::{DoubleRatchetParticipant, DoubleRatchetParticipantJson, MessageCiphertext, P2PChannelEnvelope};
|
||||
use super::feldman::{Feldman, vec_to_array, FeldmanReveal};
|
||||
use super::x3dh::{receiver_x3dh, sender_x3dh};
|
||||
|
||||
const TRIPLE_RATCHET_PROTOCOL_VERSION: u16 = 1;
|
||||
const TRIPLE_RATCHET_PROTOCOL: u16 = 2 << 8 + TRIPLE_RATCHET_PROTOCOL_VERSION;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum TripleRatchetRound {
|
||||
Uninitialized,
|
||||
Initialized,
|
||||
Committed,
|
||||
Revealed,
|
||||
Reconstructed,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TripleRatchetError {
|
||||
#[error("Crypto error: {0}")]
|
||||
CryptoError(String),
|
||||
#[error("Invalid data: {0}")]
|
||||
InvalidData(String),
|
||||
#[error("Skip limit exceeded")]
|
||||
SkipLimitExceeded,
|
||||
#[error("Malformed header")]
|
||||
MalformedHeader,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct PeerInfo {
|
||||
pub(crate) public_key: Vec<u8>,
|
||||
pub(crate) identity_public_key: Vec<u8>,
|
||||
pub(crate) signed_pre_public_key: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct TripleRatchetParticipant {
|
||||
peer_key: Scalar,
|
||||
sending_ephemeral_private_key: Scalar,
|
||||
receiving_ephemeral_keys: HashMap<Vec<u8>, Scalar>,
|
||||
receiving_group_key: Option<Vec<u8>>,
|
||||
root_key: Vec<u8>,
|
||||
sending_chain_key: Vec<u8>,
|
||||
current_header_key: Vec<u8>,
|
||||
next_header_key: Vec<u8>,
|
||||
receiving_chain_key: HashMap<Vec<u8>, Vec<u8>>,
|
||||
current_sending_chain_length: u32,
|
||||
previous_sending_chain_length: u32,
|
||||
current_receiving_chain_length: HashMap<Vec<u8>, u32>,
|
||||
previous_receiving_chain_length: HashMap<Vec<u8>, u32>,
|
||||
peer_id_map: HashMap<Vec<u8>, usize>,
|
||||
id_peer_map: HashMap<usize, PeerInfo>,
|
||||
skipped_keys_map: HashMap<Vec<u8>, HashMap<Vec<u8>, HashMap<u32, Vec<u8>>>>,
|
||||
peer_channels: HashMap<Vec<u8>, DoubleRatchetParticipant>,
|
||||
dkg_ratchet: Feldman,
|
||||
next_dkg_ratchet: Feldman,
|
||||
async_dkg_ratchet: bool,
|
||||
should_ratchet: bool,
|
||||
should_dkg_ratchet: HashMap<Vec<u8>, bool>,
|
||||
async_dkg_pubkey: Option<EdwardsPoint>,
|
||||
threshold: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PeerInfoJson {
|
||||
public_key: String,
|
||||
identity_public_key: String,
|
||||
signed_pre_public_key: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct TripleRatchetParticipantJson {
|
||||
peer_key: String,
|
||||
sending_ephemeral_private_key: String,
|
||||
receiving_ephemeral_keys: HashMap<String, String>,
|
||||
receiving_group_key: Option<String>,
|
||||
root_key: String,
|
||||
sending_chain_key: String,
|
||||
current_header_key: String,
|
||||
next_header_key: String,
|
||||
receiving_chain_key: HashMap<String, String>,
|
||||
current_sending_chain_length: u32,
|
||||
previous_sending_chain_length: u32,
|
||||
current_receiving_chain_length: HashMap<String, u32>,
|
||||
previous_receiving_chain_length: HashMap<String, u32>,
|
||||
peer_id_map: HashMap<String, usize>,
|
||||
id_peer_map: HashMap<usize, PeerInfoJson>,
|
||||
skipped_keys_map: HashMap<String, HashMap<String, HashMap<u32, String>>>,
|
||||
peer_channels: HashMap<String, String>,
|
||||
dkg_ratchet: String,
|
||||
next_dkg_ratchet: String,
|
||||
async_dkg_ratchet: bool,
|
||||
should_ratchet: bool,
|
||||
should_dkg_ratchet: HashMap<String, bool>,
|
||||
async_dkg_pubkey: Option<String>,
|
||||
threshold: usize,
|
||||
}
|
||||
|
||||
impl TripleRatchetParticipant {
|
||||
pub fn new(
|
||||
peers: &[PeerInfo],
|
||||
peer_key: Scalar,
|
||||
identity_key: Scalar,
|
||||
signed_pre_key: Scalar,
|
||||
threshold: usize,
|
||||
async_dkg_ratchet: bool,
|
||||
) -> Result<(Self, HashMap<Vec<u8>, P2PChannelEnvelope>), TripleRatchetError> {
|
||||
let mut participant = TripleRatchetParticipant {
|
||||
peer_key,
|
||||
sending_ephemeral_private_key: Scalar::random(&mut OsRng),
|
||||
receiving_ephemeral_keys: HashMap::new(),
|
||||
receiving_group_key: None,
|
||||
root_key: vec![],
|
||||
sending_chain_key: vec![],
|
||||
current_header_key: vec![],
|
||||
next_header_key: vec![],
|
||||
receiving_chain_key: HashMap::new(),
|
||||
current_sending_chain_length: 0,
|
||||
previous_sending_chain_length: 0,
|
||||
current_receiving_chain_length: HashMap::new(),
|
||||
previous_receiving_chain_length: HashMap::new(),
|
||||
peer_id_map: HashMap::new(),
|
||||
id_peer_map: HashMap::new(),
|
||||
skipped_keys_map: HashMap::new(),
|
||||
peer_channels: HashMap::new(),
|
||||
dkg_ratchet: Feldman::new(
|
||||
threshold,
|
||||
peers.len() + 1,
|
||||
0, // This will be set later
|
||||
Scalar::random(&mut OsRng),
|
||||
EdwardsPoint::generator(),
|
||||
),
|
||||
next_dkg_ratchet: Feldman::new(
|
||||
threshold,
|
||||
peers.len() + 1,
|
||||
0, // This will be set later
|
||||
Scalar::random(&mut OsRng),
|
||||
EdwardsPoint::generator(),
|
||||
),
|
||||
async_dkg_ratchet: async_dkg_ratchet,
|
||||
should_ratchet: true,
|
||||
should_dkg_ratchet: HashMap::new(),
|
||||
async_dkg_pubkey: None,
|
||||
threshold: threshold,
|
||||
};
|
||||
|
||||
let mut peer_basis = peers.to_vec();
|
||||
peer_basis.push(PeerInfo {
|
||||
public_key: (peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
identity_public_key: (identity_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
signed_pre_public_key: (signed_pre_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(),
|
||||
});
|
||||
|
||||
peer_basis.sort_by(|a, b| a.public_key.cmp(&b.public_key));
|
||||
|
||||
let mut init_messages = HashMap::new();
|
||||
let mut sender = false;
|
||||
|
||||
for (i, peer) in peer_basis.iter().enumerate() {
|
||||
participant.peer_id_map.insert(peer.public_key.clone(), i + 1);
|
||||
participant.id_peer_map.insert(i + 1, peer.clone());
|
||||
|
||||
if peer.public_key.ct_eq(&(peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec()).into() {
|
||||
sender = true;
|
||||
participant.dkg_ratchet.set_id(i + 1);
|
||||
participant.next_dkg_ratchet.set_id(i + 1);
|
||||
} else {
|
||||
participant.skipped_keys_map.insert(peer.public_key.clone(), HashMap::new());
|
||||
participant.current_receiving_chain_length.insert(peer.public_key.clone(), 0);
|
||||
participant.previous_receiving_chain_length.insert(peer.public_key.clone(), 0);
|
||||
|
||||
let session_key = if sender {
|
||||
sender_x3dh(
|
||||
&identity_key,
|
||||
&signed_pre_key,
|
||||
&EdwardsPoint::from_bytes(peer.identity_public_key.as_slice().into()).unwrap(),
|
||||
&EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
|
||||
96,
|
||||
)
|
||||
} else {
|
||||
receiver_x3dh(
|
||||
&identity_key,
|
||||
&signed_pre_key,
|
||||
&EdwardsPoint::from_bytes(peer.identity_public_key.as_slice().into()).unwrap(),
|
||||
&EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
|
||||
96,
|
||||
)
|
||||
}.unwrap();
|
||||
|
||||
let peer_channel = DoubleRatchetParticipant::new(
|
||||
&session_key[..32],
|
||||
&session_key[32..64],
|
||||
&session_key[64..],
|
||||
sender,
|
||||
signed_pre_key.clone(),
|
||||
EdwardsPoint::from_bytes(peer.signed_pre_public_key.as_slice().into()).unwrap(),
|
||||
).unwrap();
|
||||
|
||||
participant.peer_channels.insert(peer.public_key.clone(), peer_channel);
|
||||
|
||||
if sender {
|
||||
let init_message = participant.peer_channels
|
||||
.get_mut(&peer.public_key)
|
||||
.unwrap()
|
||||
.ratchet_encrypt(b"init").unwrap();
|
||||
init_messages.insert(peer.public_key.clone(), init_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((participant, init_messages))
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||
let triple_ratchet_json = TripleRatchetParticipantJson {
|
||||
peer_key: BASE64_STANDARD.encode(self.peer_key.to_bytes()),
|
||||
sending_ephemeral_private_key: BASE64_STANDARD.encode(self.sending_ephemeral_private_key.to_bytes()),
|
||||
receiving_ephemeral_keys: self.receiving_ephemeral_keys.iter()
|
||||
.map(|(k, v)| (BASE64_STANDARD.encode(k), BASE64_STANDARD.encode(v.to_bytes())))
|
||||
.collect(),
|
||||
receiving_group_key: self.receiving_group_key.as_ref().map(|k| BASE64_STANDARD.encode(k)),
|
||||
root_key: BASE64_STANDARD.encode(&self.root_key),
|
||||
sending_chain_key: BASE64_STANDARD.encode(&self.sending_chain_key),
|
||||
current_header_key: BASE64_STANDARD.encode(&self.current_header_key),
|
||||
next_header_key: BASE64_STANDARD.encode(&self.next_header_key),
|
||||
receiving_chain_key: self.receiving_chain_key.iter()
|
||||
.map(|(k, v)| (BASE64_STANDARD.encode(k), BASE64_STANDARD.encode(v)))
|
||||
.collect(),
|
||||
current_sending_chain_length: self.current_sending_chain_length,
|
||||
previous_sending_chain_length: self.previous_sending_chain_length,
|
||||
current_receiving_chain_length: self.current_receiving_chain_length.iter()
|
||||
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
|
||||
.collect(),
|
||||
previous_receiving_chain_length: self.previous_receiving_chain_length.iter()
|
||||
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
|
||||
.collect(),
|
||||
peer_id_map: self.peer_id_map.iter()
|
||||
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
|
||||
.collect(),
|
||||
id_peer_map: self.id_peer_map.iter()
|
||||
.map(|(&k, v)| (k, PeerInfoJson {
|
||||
public_key: BASE64_STANDARD.encode(&v.public_key),
|
||||
identity_public_key: BASE64_STANDARD.encode(&v.identity_public_key),
|
||||
signed_pre_public_key: BASE64_STANDARD.encode(&v.signed_pre_public_key),
|
||||
}))
|
||||
.collect(),
|
||||
skipped_keys_map: self.skipped_keys_map.iter()
|
||||
.map(|(k1, v1)| (BASE64_STANDARD.encode(k1),
|
||||
v1.iter().map(|(k2, v2)| (BASE64_STANDARD.encode(k2),
|
||||
v2.iter().map(|(&k3, v3)| (k3, BASE64_STANDARD.encode(v3)))
|
||||
.collect()))
|
||||
.collect()))
|
||||
.collect(),
|
||||
peer_channels: self.peer_channels.iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.encode(k), v.to_json()?)))
|
||||
.collect::<Result<HashMap<_, _>, serde_json::Error>>()?,
|
||||
dkg_ratchet: self.dkg_ratchet.to_json()?,
|
||||
next_dkg_ratchet: self.next_dkg_ratchet.to_json()?,
|
||||
async_dkg_ratchet: self.async_dkg_ratchet,
|
||||
should_ratchet: self.should_ratchet,
|
||||
should_dkg_ratchet: self.should_dkg_ratchet.iter()
|
||||
.map(|(k, &v)| (BASE64_STANDARD.encode(k), v))
|
||||
.collect(),
|
||||
async_dkg_pubkey: self.async_dkg_pubkey.as_ref()
|
||||
.map(|p| BASE64_STANDARD.encode(p.compress().to_bytes())),
|
||||
threshold: self.threshold,
|
||||
};
|
||||
|
||||
serde_json::to_string(&triple_ratchet_json)
|
||||
}
|
||||
|
||||
pub fn from_json(json: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let triple_ratchet_json: TripleRatchetParticipantJson = serde_json::from_str(json)?;
|
||||
|
||||
let peer_key_bytes = BASE64_STANDARD.decode(&triple_ratchet_json.peer_key)?;
|
||||
let peer_key = Scalar::from_bytes(&vec_to_array::<56>(peer_key_bytes)?);
|
||||
|
||||
let sending_ephemeral_private_key_bytes = BASE64_STANDARD.decode(&triple_ratchet_json.sending_ephemeral_private_key)?;
|
||||
let sending_ephemeral_private_key = Scalar::from_bytes(&vec_to_array::<56>(sending_ephemeral_private_key_bytes)?);
|
||||
|
||||
let receiving_ephemeral_keys = triple_ratchet_json.receiving_ephemeral_keys.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let key = BASE64_STANDARD.decode(k)?;
|
||||
let value_bytes = BASE64_STANDARD.decode(v)?;
|
||||
let value = Scalar::from_bytes(&vec_to_array::<56>(value_bytes)?);
|
||||
Ok((key, value))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let receiving_group_key = triple_ratchet_json.receiving_group_key
|
||||
.map(|k| BASE64_STANDARD.decode(k))
|
||||
.transpose()?;
|
||||
|
||||
let root_key = BASE64_STANDARD.decode(&triple_ratchet_json.root_key)?;
|
||||
let sending_chain_key = BASE64_STANDARD.decode(&triple_ratchet_json.sending_chain_key)?;
|
||||
let current_header_key = BASE64_STANDARD.decode(&triple_ratchet_json.current_header_key)?;
|
||||
let next_header_key = BASE64_STANDARD.decode(&triple_ratchet_json.next_header_key)?;
|
||||
|
||||
let receiving_chain_key = triple_ratchet_json.receiving_chain_key.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let key = BASE64_STANDARD.decode(k)?;
|
||||
let value = BASE64_STANDARD.decode(v)?;
|
||||
Ok((key, value))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let current_receiving_chain_length = triple_ratchet_json.current_receiving_chain_length.into_iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let previous_receiving_chain_length = triple_ratchet_json.previous_receiving_chain_length.into_iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let peer_id_map = triple_ratchet_json.peer_id_map.into_iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let id_peer_map = triple_ratchet_json.id_peer_map.into_iter()
|
||||
.map(|(k, v)| Ok((k, PeerInfo {
|
||||
public_key: BASE64_STANDARD.decode(&v.public_key)?,
|
||||
identity_public_key: BASE64_STANDARD.decode(&v.identity_public_key)?,
|
||||
signed_pre_public_key: BASE64_STANDARD.decode(&v.signed_pre_public_key)?,
|
||||
})))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let skipped_keys_map = triple_ratchet_json.skipped_keys_map.into_iter()
|
||||
.map(|(k1, v1)| {
|
||||
let key1 = BASE64_STANDARD.decode(k1)?;
|
||||
let value1 = v1.into_iter()
|
||||
.map(|(k2, v2)| {
|
||||
let key2 = BASE64_STANDARD.decode(k2)?;
|
||||
let value2 = v2.into_iter()
|
||||
.map(|(k3, v3)| Ok((k3, BASE64_STANDARD.decode(v3)?)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
Ok((key2, value2))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
Ok((key1, value1))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let peer_channels = triple_ratchet_json.peer_channels.into_iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, DoubleRatchetParticipant::from_json(v)?)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let dkg_ratchet = Feldman::from_json(&triple_ratchet_json.dkg_ratchet)?;
|
||||
let next_dkg_ratchet = Feldman::from_json(&triple_ratchet_json.next_dkg_ratchet)?;
|
||||
|
||||
let should_dkg_ratchet = triple_ratchet_json.should_dkg_ratchet.into_iter()
|
||||
.map(|(k, v)| Ok((BASE64_STANDARD.decode(k)?, v)))
|
||||
.collect::<Result<HashMap<_, _>, Box<dyn std::error::Error>>>()?;
|
||||
|
||||
let mut async_dkg_pubkey: Option<EdwardsPoint> = None;
|
||||
|
||||
if triple_ratchet_json.async_dkg_pubkey.is_some() {
|
||||
let bytes = BASE64_STANDARD.decode(triple_ratchet_json.async_dkg_pubkey.unwrap())?;
|
||||
let point = EdwardsPoint::from_bytes(&vec_to_array::<57>(bytes)?.into());
|
||||
async_dkg_pubkey = point.into_option();
|
||||
}
|
||||
|
||||
Ok(TripleRatchetParticipant {
|
||||
peer_key,
|
||||
sending_ephemeral_private_key,
|
||||
receiving_ephemeral_keys,
|
||||
receiving_group_key,
|
||||
root_key,
|
||||
sending_chain_key,
|
||||
current_header_key,
|
||||
next_header_key,
|
||||
receiving_chain_key,
|
||||
current_sending_chain_length: triple_ratchet_json.current_sending_chain_length,
|
||||
previous_sending_chain_length: triple_ratchet_json.previous_sending_chain_length,
|
||||
current_receiving_chain_length,
|
||||
previous_receiving_chain_length,
|
||||
peer_id_map,
|
||||
id_peer_map,
|
||||
skipped_keys_map,
|
||||
peer_channels,
|
||||
dkg_ratchet,
|
||||
next_dkg_ratchet,
|
||||
async_dkg_ratchet: triple_ratchet_json.async_dkg_ratchet,
|
||||
should_ratchet: triple_ratchet_json.should_ratchet,
|
||||
should_dkg_ratchet,
|
||||
async_dkg_pubkey,
|
||||
threshold: triple_ratchet_json.threshold,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_peer_id_map(&self) -> HashMap<Vec<u8>, usize> {
|
||||
return self.peer_id_map.clone();
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self, init_messages: &HashMap<Vec<u8>, P2PChannelEnvelope>)
|
||||
-> Result<HashMap<Vec<u8>, P2PChannelEnvelope>, TripleRatchetError> {
|
||||
for (k, m) in init_messages {
|
||||
let msg = self.peer_channels.get_mut(k).unwrap().ratchet_decrypt(m).unwrap();
|
||||
if msg != b"init" {
|
||||
return Err(TripleRatchetError::InvalidData("Invalid init message".into()));
|
||||
}
|
||||
}
|
||||
|
||||
self.dkg_ratchet.sample_polynomial(&mut OsRng);
|
||||
|
||||
let result = self.dkg_ratchet.get_poly_frags().unwrap();
|
||||
|
||||
let mut result_map = HashMap::new();
|
||||
for (k, v) in result {
|
||||
let test: bool = self.id_peer_map[&k].public_key.ct_eq(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes()).into();
|
||||
if !test {
|
||||
let envelope = self.peer_channels
|
||||
.get_mut(&self.id_peer_map[&k].public_key)
|
||||
.unwrap()
|
||||
.ratchet_encrypt(&v);
|
||||
result_map.insert(self.id_peer_map[&k].public_key.clone(), envelope.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result_map)
|
||||
}
|
||||
|
||||
pub fn receive_poly_frag(&mut self, peer_id: &[u8], frag: &P2PChannelEnvelope)
|
||||
-> Result<Option<HashMap<Vec<u8>, P2PChannelEnvelope>>, TripleRatchetError> {
|
||||
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(frag).unwrap();
|
||||
|
||||
let result = self.dkg_ratchet.set_poly_frag_for_party(
|
||||
*self.peer_id_map.get(peer_id).unwrap(),
|
||||
&b,
|
||||
).unwrap();
|
||||
|
||||
if result.is_some() {
|
||||
let mut envelopes = HashMap::new();
|
||||
for (k, c) in &mut self.peer_channels {
|
||||
|
||||
let envelope = c.ratchet_encrypt(&result.clone().unwrap()).unwrap();
|
||||
envelopes.insert(k.clone(), envelope);
|
||||
}
|
||||
Ok(Some(envelopes))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_commitment(&mut self, peer_id: &[u8], zkcommit: &P2PChannelEnvelope)
|
||||
-> Result<Option<HashMap<Vec<u8>, P2PChannelEnvelope>>, TripleRatchetError> {
|
||||
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(zkcommit).unwrap();
|
||||
|
||||
let result = self.dkg_ratchet.receive_commitments(
|
||||
*self.peer_id_map.get(peer_id).unwrap(),
|
||||
&b,
|
||||
).unwrap();
|
||||
|
||||
if let Some(reveal) = result {
|
||||
let d = serde_json::to_vec(&reveal).unwrap();
|
||||
let mut envelopes = HashMap::new();
|
||||
for (k, c) in &mut self.peer_channels {
|
||||
let envelope = c.ratchet_encrypt(&d).unwrap();
|
||||
envelopes.insert(k.clone(), envelope);
|
||||
}
|
||||
Ok(Some(envelopes))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recombine(&mut self, peer_id: &[u8], reveal: &P2PChannelEnvelope) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let b = self.peer_channels.get_mut(peer_id).unwrap().ratchet_decrypt(reveal).unwrap();
|
||||
|
||||
let rev: FeldmanReveal = serde_json::from_slice(&b).unwrap();
|
||||
|
||||
let done = self.dkg_ratchet.recombine(
|
||||
*self.peer_id_map.get(peer_id).unwrap(),
|
||||
&rev,
|
||||
).unwrap();
|
||||
|
||||
if !done {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
|
||||
let hkdf = Hkdf::<Sha512>::new(
|
||||
Some(&sess),
|
||||
&self.dkg_ratchet.public_key_bytes(),
|
||||
);
|
||||
let mut rkck = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
self.root_key = rkck[..32].to_vec();
|
||||
self.current_header_key = rkck[32..64].to_vec();
|
||||
self.next_header_key = rkck[64..].to_vec();
|
||||
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ratchet_encrypt(&mut self, message: &[u8]) -> Result<P2PChannelEnvelope, Box<dyn std::error::Error>> {
|
||||
if self.should_ratchet {
|
||||
self.ratchet_sender_ephemeral_keys()?;
|
||||
}
|
||||
|
||||
if self.async_dkg_ratchet && self.async_dkg_pubkey.is_some() && self.should_dkg_ratchet.len() == self.threshold {
|
||||
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
|
||||
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
|
||||
let hkdf = Hkdf::<Sha512>::new(
|
||||
Some(&sess),
|
||||
&self.dkg_ratchet.public_key_bytes(),
|
||||
);
|
||||
let mut rkck = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
self.root_key = rkck[..32].to_vec();
|
||||
self.current_header_key = rkck[32..64].to_vec();
|
||||
self.next_header_key = rkck[64..].to_vec();
|
||||
self.async_dkg_pubkey = None;
|
||||
}
|
||||
|
||||
let dkg_pub: Option<(Vec<u8>, Vec<u8>)> = if (self.async_dkg_ratchet && self.should_dkg_ratchet.len() > 0) || self.async_dkg_pubkey.is_some() {
|
||||
if self.async_dkg_pubkey.is_none() {
|
||||
self.should_dkg_ratchet = HashMap::new();
|
||||
self.async_dkg_pubkey = Some(Scalar::random(&mut OsRng) * EdwardsPoint::generator());
|
||||
}
|
||||
|
||||
let pubkey = self.async_dkg_pubkey.unwrap();
|
||||
let invpub = self.dkg_ratchet.mul_share(&pubkey.compress().to_bytes());
|
||||
if invpub.is_err() {
|
||||
return Err(Box::new(invpub.unwrap_err()));
|
||||
}
|
||||
self.should_dkg_ratchet.insert((self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec(), true);
|
||||
|
||||
Some((invpub.unwrap(), pubkey.compress().to_bytes().to_vec()))
|
||||
} else { None };
|
||||
|
||||
let mut envelope = P2PChannelEnvelope {
|
||||
protocol_identifier: TRIPLE_RATCHET_PROTOCOL,
|
||||
message_header: MessageCiphertext::default(),
|
||||
message_body: MessageCiphertext::default(),
|
||||
};
|
||||
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(&self.sending_chain_key);
|
||||
|
||||
self.sending_chain_key = new_chain_key;
|
||||
|
||||
let header = self.encode_header(dkg_pub);
|
||||
envelope.message_header = self.encrypt(&header, &self.current_header_key, None)?;
|
||||
envelope.message_body = self.encrypt(
|
||||
message,
|
||||
&message_key,
|
||||
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
|
||||
)?;
|
||||
|
||||
self.current_sending_chain_length += 1;
|
||||
|
||||
Ok(envelope)
|
||||
}
|
||||
|
||||
pub fn ratchet_decrypt(&mut self, envelope: &P2PChannelEnvelope) -> Result<(Vec<u8>, bool), Box<dyn std::error::Error>> {
|
||||
if let Some(plaintext) = self.try_skipped_message_keys(envelope)? {
|
||||
return Ok((plaintext, false));
|
||||
}
|
||||
|
||||
let header_key = self.current_header_key.clone();
|
||||
let (header, mut should_dkg_ratchet, should_advance_dkg_ratchet) = self.decrypt_header(&envelope.message_header, &header_key)?;
|
||||
|
||||
let (sender_key, receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length, dkg_pub) =
|
||||
self.decode_header(&header)?;
|
||||
|
||||
let should_ratchet = self.receiving_ephemeral_keys.get(&sender_key.compress().to_bytes().to_vec()).map(|k| !k.eq(&receiving_ephemeral_key)).unwrap_or(true);
|
||||
if should_ratchet {
|
||||
self.skip_message_keys(&sender_key, previous_receiving_chain_length)?;
|
||||
self.ratchet_receiver_ephemeral_keys(&sender_key, &receiving_ephemeral_key)?;
|
||||
}
|
||||
|
||||
self.skip_message_keys(&sender_key, current_receiving_chain_length)?;
|
||||
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(
|
||||
&self.receiving_chain_key[&sender_key.compress().to_bytes().to_vec()],
|
||||
);
|
||||
|
||||
self.receiving_chain_key.insert(sender_key.compress().to_bytes().to_vec(), new_chain_key);
|
||||
|
||||
*self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0) += 1;
|
||||
|
||||
if should_advance_dkg_ratchet {
|
||||
self.receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
|
||||
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
|
||||
let hkdf = Hkdf::<Sha512>::new(
|
||||
Some(&sess),
|
||||
&self.dkg_ratchet.public_key_bytes(),
|
||||
);
|
||||
let mut rkck = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
self.root_key = rkck[..32].to_vec();
|
||||
self.current_header_key = rkck[32..64].to_vec();
|
||||
self.next_header_key = rkck[64..].to_vec();
|
||||
}
|
||||
|
||||
let plaintext = self.decrypt(
|
||||
&envelope.message_body,
|
||||
&message_key,
|
||||
Some(&[&aead_key[..], &envelope.message_header.ciphertext[..]].concat()),
|
||||
)?;
|
||||
|
||||
if dkg_pub.is_some() {
|
||||
should_dkg_ratchet = false;
|
||||
let (other_invpub, pubkey) = dkg_pub.unwrap();
|
||||
self.async_dkg_pubkey = Some(pubkey);
|
||||
|
||||
let invpub = self.dkg_ratchet.mul_share(&pubkey.compress().to_bytes());
|
||||
if invpub.is_err() {
|
||||
return Err(Box::new(invpub.unwrap_err()));
|
||||
}
|
||||
|
||||
let invvec = invpub.unwrap();
|
||||
let inv = invvec.as_slice();
|
||||
let other = &other_invpub.compress().to_bytes();
|
||||
|
||||
let other_id = self.peer_id_map.get(&sender_key.compress().to_bytes().to_vec());
|
||||
if other_id.is_none() {
|
||||
return Err(Box::new(TripleRatchetError::MalformedHeader))
|
||||
}
|
||||
|
||||
let our_id = self.peer_id_map.get(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec());
|
||||
let (shares, ids) = if our_id.unwrap() < other_id.unwrap() {
|
||||
(vec![inv, other], [*our_id.unwrap(), *other_id.unwrap()])
|
||||
} else {
|
||||
(vec![other, inv], [*other_id.unwrap(), *our_id.unwrap()])
|
||||
};
|
||||
|
||||
let next_pub = self.dkg_ratchet.combine_mul_share(shares, &ids);
|
||||
if next_pub.is_err() {
|
||||
return Err(Box::new(next_pub.unwrap_err()));
|
||||
}
|
||||
|
||||
let next = EdwardsPoint::from_bytes(next_pub.unwrap().as_slice().into());
|
||||
if next.is_none().into() {
|
||||
return Err(Box::new(TripleRatchetError::MalformedHeader));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((plaintext, should_dkg_ratchet))
|
||||
}
|
||||
|
||||
fn ratchet_sender_ephemeral_keys(&mut self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let receiving_group_key = self.receiving_group_key.as_ref().ok_or_else(|| TripleRatchetError::CryptoError("Receiving group key not set".into()))?;
|
||||
self.sending_ephemeral_private_key = Scalar::random(&mut OsRng);
|
||||
let dh_output = (self.sending_ephemeral_private_key * EdwardsPoint::from_bytes(receiving_group_key.as_slice().into()).unwrap()).compress().to_bytes();
|
||||
|
||||
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output);
|
||||
let mut rkck2 = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck2);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
self.root_key = rkck2[..32].to_vec();
|
||||
self.sending_chain_key = rkck2[32..64].to_vec();
|
||||
self.should_ratchet = false;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ratchet_receiver_ephemeral_keys(&mut self, peer_key: &EdwardsPoint, new_ephemeral_key: &Scalar) -> Result<(), Box<dyn std::error::Error>> {
|
||||
self.previous_sending_chain_length = self.current_sending_chain_length;
|
||||
self.current_sending_chain_length = 0;
|
||||
*self.current_receiving_chain_length.entry(peer_key.compress().to_bytes().to_vec()).or_insert(0) = 0;
|
||||
self.receiving_ephemeral_keys.insert(peer_key.compress().to_bytes().to_vec(), new_ephemeral_key.clone());
|
||||
|
||||
let receiving_group_key = self.receiving_group_key.as_ref().ok_or_else(|| TripleRatchetError::CryptoError("Receiving group key not set".into()))?;
|
||||
let dh_output = (new_ephemeral_key * EdwardsPoint::from_bytes(receiving_group_key.as_slice().into()).unwrap()).compress().to_bytes();
|
||||
|
||||
let hkdf = Hkdf::<Sha512>::new(Some(&self.root_key), &dh_output);
|
||||
let mut rkck = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
self.root_key = rkck[..32].to_vec();
|
||||
self.receiving_chain_key.insert(peer_key.compress().to_bytes().to_vec(), rkck[32..64].to_vec());
|
||||
self.should_ratchet = true;
|
||||
|
||||
if self.async_dkg_ratchet {
|
||||
self.should_dkg_ratchet = HashMap::new();
|
||||
self.should_dkg_ratchet.insert(peer_key.compress().to_bytes().to_vec(), true);
|
||||
self.async_dkg_pubkey = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_skipped_message_keys(&mut self, envelope: &P2PChannelEnvelope) -> Result<Option<Vec<u8>>, Box<dyn std::error::Error>> {
|
||||
for (receiving_header_key, skipped_keys) in &self.skipped_keys_map.clone() {
|
||||
if let Ok((header, _, _)) = self.decrypt_header(&envelope.message_header, receiving_header_key) {
|
||||
let (peer_key, _, _, current, _) = self.decode_header(&header)?;
|
||||
if let Some(peer_skipped_keys) = skipped_keys.get(&peer_key.compress().to_bytes().to_vec()) {
|
||||
if let Some(key_pair) = peer_skipped_keys.get(¤t) {
|
||||
let message_key = &key_pair[..32];
|
||||
let aead_key = &key_pair[32..];
|
||||
let plaintext = self.decrypt(
|
||||
&envelope.message_body,
|
||||
message_key,
|
||||
Some(&[aead_key, &envelope.message_header.ciphertext].concat()),
|
||||
)?;
|
||||
return Ok(Some(plaintext));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn skip_message_keys(&mut self, sender_key: &EdwardsPoint, until: u32) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut current = *self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0);
|
||||
if current + 100 < until {
|
||||
return Err(Box::new(TripleRatchetError::SkipLimitExceeded));
|
||||
}
|
||||
|
||||
if let Some(chain_key) = self.receiving_chain_key.get_mut(&sender_key.compress().to_bytes().to_vec()) {
|
||||
while current < until {
|
||||
let (new_chain_key, message_key, aead_key) = ratchet_keys(chain_key);
|
||||
self.skipped_keys_map
|
||||
.entry(self.current_header_key.clone())
|
||||
.or_insert_with(HashMap::new)
|
||||
.entry(sender_key.compress().to_bytes().to_vec())
|
||||
.or_insert_with(HashMap::new)
|
||||
.insert(current, [message_key, aead_key].concat());
|
||||
*chain_key = new_chain_key;
|
||||
current += 1;
|
||||
*self.current_receiving_chain_length.entry(sender_key.compress().to_bytes().to_vec()).or_insert(0) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encode_header(&self, dkg_pub: Option<(Vec<u8>, Vec<u8>)>) -> Vec<u8> {
|
||||
let mut header = Vec::new();
|
||||
header.extend_from_slice(&(self.peer_key * EdwardsPoint::generator()).compress().to_bytes().to_vec());
|
||||
header.extend_from_slice(&self.sending_ephemeral_private_key.to_bytes());
|
||||
header.extend_from_slice(&self.previous_sending_chain_length.to_be_bytes());
|
||||
header.extend_from_slice(&self.current_sending_chain_length.to_be_bytes());
|
||||
if dkg_pub.is_some() {
|
||||
let (invpub, pubkey) = dkg_pub.unwrap();
|
||||
header.extend(invpub);
|
||||
header.extend(pubkey);
|
||||
}
|
||||
header
|
||||
}
|
||||
|
||||
fn decrypt_header(&mut self, ciphertext: &MessageCiphertext, receiving_header_key: &[u8]) -> Result<(Vec<u8>, bool, bool), Box<dyn std::error::Error>> {
|
||||
match self.decrypt(ciphertext, receiving_header_key, None) {
|
||||
Ok(header) => {
|
||||
Ok((header, self.async_dkg_ratchet, false))
|
||||
},
|
||||
Err(_) if receiving_header_key == self.current_header_key => {
|
||||
if self.async_dkg_ratchet && self.async_dkg_pubkey.is_some() {
|
||||
let receiving_group_key = Some(self.dkg_ratchet.public_key().to_bytes().to_vec());
|
||||
let sess = Sha512::digest(&self.dkg_ratchet.public_key_bytes());
|
||||
let hkdf = Hkdf::<Sha512>::new(
|
||||
Some(&sess),
|
||||
&self.dkg_ratchet.public_key_bytes(),
|
||||
);
|
||||
let mut rkck = [0u8; 96];
|
||||
let result = hkdf.expand(b"quilibrium-triple-ratchet", &mut rkck);
|
||||
if result.is_err() {
|
||||
return Err(Box::new(TripleRatchetError::CryptoError("invalid length".to_owned())));
|
||||
}
|
||||
|
||||
let current_header_key = rkck[32..64].to_vec();
|
||||
match self.decrypt(ciphertext, ¤t_header_key, None) {
|
||||
Ok(header) => Ok((header, true, true)),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
} else {
|
||||
match self.decrypt(ciphertext, &self.next_header_key, None) {
|
||||
Ok(header) => Ok((header, true, false)),
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => Err(Box::new(e)),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_header(&self, header: &[u8]) -> Result<(EdwardsPoint, Scalar, u32, u32, Option<(EdwardsPoint, EdwardsPoint)>), TripleRatchetError> {
|
||||
if header.len() < 121 {
|
||||
return Err(TripleRatchetError::MalformedHeader);
|
||||
}
|
||||
|
||||
let sender_key = EdwardsPoint::from_bytes(header[..57].into()).unwrap();
|
||||
let receiving_ephemeral_key = Scalar::from_bytes(header[57..113].try_into().unwrap());
|
||||
let previous_receiving_chain_length = u32::from_be_bytes(header[113..117].try_into().unwrap());
|
||||
let current_receiving_chain_length = u32::from_be_bytes(header[117..121].try_into().unwrap());
|
||||
|
||||
let dkg_pub = if header.len() == 235 {
|
||||
let invpub = EdwardsPoint::from_bytes(header[121..178].into()).into_option();
|
||||
let pubkey = EdwardsPoint::from_bytes(header[178..].into()).into_option();
|
||||
if invpub.is_none() || pubkey.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some((invpub.unwrap(), pubkey.unwrap()))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((sender_key, receiving_ephemeral_key, previous_receiving_chain_length, current_receiving_chain_length, dkg_pub))
|
||||
}
|
||||
|
||||
fn encrypt(&self, plaintext: &[u8], key: &[u8], associated_data: Option<&[u8]>)
|
||||
-> Result<MessageCiphertext, Box<dyn std::error::Error>> {
|
||||
use aes_gcm::KeyInit;
|
||||
let mut iv = [0u8; 12];
|
||||
OsRng.fill_bytes(&mut iv);
|
||||
|
||||
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
|
||||
let nonce = Nonce::from_slice(&iv);
|
||||
|
||||
let mut associated_data = associated_data.unwrap_or(&[]);
|
||||
let mut aad = [0u8; 32];
|
||||
if associated_data.len() == 0 {
|
||||
OsRng.fill_bytes(&mut aad);
|
||||
associated_data = &aad
|
||||
}
|
||||
|
||||
let ciphertext = cipher.encrypt(nonce, Payload{
|
||||
msg: plaintext,
|
||||
aad: associated_data,
|
||||
}).map_err(|e| format!("Encryption failed: {}", e))?;
|
||||
|
||||
Ok(MessageCiphertext {
|
||||
ciphertext,
|
||||
initialization_vector: iv.to_vec(),
|
||||
associated_data: Some(associated_data.to_vec()),
|
||||
})
|
||||
}
|
||||
|
||||
fn decrypt(&self, ciphertext: &MessageCiphertext, key: &[u8], associated_data: Option<&[u8]>) -> Result<Vec<u8>, TripleRatchetError> {
|
||||
use aes_gcm::KeyInit;
|
||||
if key.len() != 32 {
|
||||
return Err(TripleRatchetError::InvalidData("Invalid key length".to_string()));
|
||||
}
|
||||
let cipher = Aes256Gcm::new_from_slice(key).unwrap();
|
||||
let nonce = Nonce::from_slice(&ciphertext.initialization_vector);
|
||||
|
||||
let associated_data = associated_data.unwrap_or_else(|| ciphertext.associated_data.as_ref().unwrap());
|
||||
|
||||
cipher.decrypt(nonce, Payload{
|
||||
msg: &ciphertext.ciphertext.as_ref(),
|
||||
aad: associated_data.as_ref(),
|
||||
}).map_err(|e| TripleRatchetError::CryptoError(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn ratchet_keys(input: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
||||
let mut output = [0u8; 96];
|
||||
let hkdf = Hkdf::<Sha512>::new(None, input);
|
||||
hkdf.expand(b"quilibrium-triple-ratchet-keys", &mut output).unwrap();
|
||||
(output[..32].to_vec(), output[32..64].to_vec(), output[64..].to_vec())
|
||||
}
|
82
crates/channel/src/protocols/x3dh.rs
Normal file
82
crates/channel/src/protocols/x3dh.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use std::collections::HashMap;
|
||||
use sha2::Sha512;
|
||||
use hkdf::Hkdf;
|
||||
use ed448_goldilocks_plus::{subtle, CompressedEdwardsY, EdwardsPoint, Scalar};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
static ref DOMAIN_SEPARATORS: HashMap<&'static str, Vec<u8>> = {
|
||||
let mut m = HashMap::new();
|
||||
m.insert("ed448", vec![
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF,
|
||||
]);
|
||||
m
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sender_x3dh(
|
||||
sending_identity_private_key: &Scalar,
|
||||
sending_ephemeral_private_key: &Scalar,
|
||||
receiving_identity_key: &EdwardsPoint,
|
||||
receiving_signed_pre_key: &EdwardsPoint,
|
||||
session_key_length: usize,
|
||||
) -> Option<Vec<u8>> {
|
||||
let xdh1 = (receiving_signed_pre_key * sending_identity_private_key).compress().to_bytes().to_vec();
|
||||
let xdh2 = (receiving_identity_key * sending_ephemeral_private_key).compress().to_bytes().to_vec();
|
||||
let xdh3 = (receiving_signed_pre_key * sending_ephemeral_private_key).compress().to_bytes().to_vec();
|
||||
|
||||
let salt = vec![0u8; session_key_length];
|
||||
let info = b"quilibrium-x3dh";
|
||||
|
||||
let domain_separator = DOMAIN_SEPARATORS.get("ed448")
|
||||
.expect("Unsupported curve");
|
||||
|
||||
let mut ikm = Vec::<u8>::new();
|
||||
ikm.extend(domain_separator);
|
||||
ikm.extend(xdh1);
|
||||
ikm.extend(xdh2);
|
||||
ikm.extend(xdh3);
|
||||
|
||||
let hk = Hkdf::<Sha512>::new(Some(&salt), &ikm);
|
||||
let mut session_key = vec![0u8; session_key_length];
|
||||
hk.expand(info, &mut session_key).ok()?;
|
||||
|
||||
Some(session_key)
|
||||
}
|
||||
|
||||
pub fn receiver_x3dh(
|
||||
sending_identity_private_key: &Scalar,
|
||||
sending_signed_pre_private_key: &Scalar,
|
||||
receiving_identity_key: &EdwardsPoint,
|
||||
receiving_ephemeral_key: &EdwardsPoint,
|
||||
session_key_length: usize,
|
||||
) -> Option<Vec<u8>> {
|
||||
let xdh1 = (receiving_identity_key * sending_signed_pre_private_key).compress().to_bytes().to_vec();
|
||||
let xdh2 = (receiving_ephemeral_key * sending_identity_private_key).compress().to_bytes().to_vec();
|
||||
let xdh3 = (receiving_ephemeral_key * sending_signed_pre_private_key).compress().to_bytes().to_vec();
|
||||
|
||||
let salt = vec![0u8; session_key_length];
|
||||
let info = b"quilibrium-x3dh";
|
||||
|
||||
let domain_separator = DOMAIN_SEPARATORS.get("ed448")
|
||||
.expect("Unsupported curve");
|
||||
|
||||
let mut ikm = Vec::<u8>::new();
|
||||
ikm.extend(domain_separator);
|
||||
ikm.extend(xdh1);
|
||||
ikm.extend(xdh2);
|
||||
ikm.extend(xdh3);
|
||||
|
||||
let hk = Hkdf::<Sha512>::new(Some(&salt), &ikm);
|
||||
let mut session_key = vec![0u8; session_key_length];
|
||||
hk.expand(info, &mut session_key).ok()?;
|
||||
|
||||
Some(session_key)
|
||||
}
|
7
crates/rpm/Cargo.lock
generated
Normal file
7
crates/rpm/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "rpm"
|
||||
version = "0.1.0"
|
30
crates/rpm/Cargo.toml
Normal file
30
crates/rpm/Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "rpm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib"]
|
||||
name = "rpm"
|
||||
|
||||
[dependencies]
|
||||
hex = "0.4.3"
|
||||
serde_json = "1.0.117"
|
||||
uniffi = { version= "0.25", features = ["cli"]}
|
||||
curve25519-dalek = "4.1.3"
|
||||
rand = "0.8.5"
|
||||
num = "0.4.3"
|
||||
lazy_static = "1.5.0"
|
||||
subtle = "2.6.1"
|
||||
rayon = "1.10"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4", features = ["html_reports"] }
|
||||
rand = "0.8.5"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.25", features = [ "build" ] }
|
||||
|
||||
[[bench]]
|
||||
name = "bench_rpm"
|
||||
harness = false
|
32
crates/rpm/README.md
Normal file
32
crates/rpm/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
# RPM
|
||||
Based on the paper [RPM: Robust Anonymity at Scale](https://eprint.iacr.org/2022/1037). Built to be used as a primitive – all sharp edges are exposed, using this without properly checking the boundaries of the input will cut!
|
||||
|
||||
Includes UniFFI UDL file for FFI integration.
|
||||
|
||||
## How to use safely
|
||||
There are six public functions of interest for builders to use if they want to have a purpose built mixnet:
|
||||
|
||||
- rpm_generate_initial_shares
|
||||
- rpm_combine_shares_and_mask
|
||||
- rpm_sketch_propose
|
||||
- rpm_sketch_verify
|
||||
- rpm_permute
|
||||
- rpm_finalize
|
||||
|
||||
### rpm_generate_initial_shares
|
||||
Performs the first of four steps of the "offline" phase of RPM Variant 3 (Subvariant 2). Each active dealer in a given offline batch generates initial shares of the permutation matrices and masks. Assumes player identifiers are in monotonically increasing order starting from 1, player count must be greater than or equal to the square of dealers. For malicious security, a ZKPoK should be used for each share set and broadcasted by all dealers prior to the next step.
|
||||
|
||||
### rpm_combine_shares_and_mask
|
||||
Performs the second of four steps of the "offline" phase. Each player should have received their respective shares and organized them according to sequence of players for inputs. For malicious security, the ZKPoK should be verified after invoking this. The splits of the permutation matrices and masks should be passed into the next step.
|
||||
|
||||
### rpm_sketch_propose
|
||||
Performs the third of four steps of the "offline" phase. Each player should take the splits of the permutation matrices and masks and pass them into this function. All players should broadcast the sketch proposals.
|
||||
|
||||
### rpm_sketch_verify
|
||||
Performs the fourth step of the "offline" phase. Each player should take the broadcasted sketch proposals and pass them into the function. If any dealer or player had cheated, it would be revealed by the intersection of failures.
|
||||
|
||||
### rpm_permute
|
||||
Performs the first step of the "online" phase. Before invoking this, each message sender should obtain a vector of the first depth's mask shares, at the same index from each, from as many players as needed to match the dealer count. The sender should combine these shares to produce the mask for their message, and add it to their message (chunked by field element size if need be), and secret share it with the same parameters (t = dealers, n = players). The players will collect their respective shares of inputs into an input vector following the order of the mask shares that were applied. The players will then pass in the input vector shares, matrix shares, mask shares, and combined matrix/mask shares from `rpm_combine_shares_and_mask`, along with the depth and player identifiers in player order. This function should be invoked in as many rounds as there is depth to the mixnet, using the previous invocation's output shares given to each respective player. This must be repeated for each chunked vector of field elements if applicable.
|
||||
|
||||
### rpm_finalize
|
||||
A convenience funciton which recombines the shares of the final invocation of `rpm_permute`'s output into the finalized vector of the mixnet.
|
71
crates/rpm/benches/bench_rpm.rs
Normal file
71
crates/rpm/benches/bench_rpm.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use curve25519_dalek::Scalar;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let smsize = 100;
|
||||
let msize = smsize*smsize;
|
||||
let depth = 4;
|
||||
let players = 9;
|
||||
let dealers = 3;
|
||||
|
||||
//todo parties should be + 1
|
||||
let is1 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
|
||||
let is2 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
|
||||
let is3 = rpm::rpm_generate_initial_shares(msize, depth, dealers, players);
|
||||
let (m1, r1) = (is1.ms, is1.rs);
|
||||
let (m2, r2) = (is2.ms, is2.rs);
|
||||
let (m3, r3) = (is3.ms, is3.rs);
|
||||
|
||||
let mut ms = vec![vec![vec![vec![vec![vec![[0u8; 32]; smsize]; smsize]; smsize]; depth]; dealers]; players];
|
||||
let mut rs = vec![vec![vec![vec![[0u8; 32]; msize]; depth]; dealers]; players];
|
||||
|
||||
let mut mc = Vec::<Vec<Vec<Vec<Vec<[u8; 32]>>>>>::with_capacity(players);
|
||||
let mut rc = Vec::<Vec<Vec<[u8; 32]>>>::with_capacity(players);
|
||||
let mut mrmc = Vec::<Vec<Vec<Vec<Vec<[u8; 32]>>>>>::with_capacity(players);
|
||||
let mut mccs = Vec::<Vec<Vec<Vec<[u8; 32]>>>>::with_capacity(players);
|
||||
let mut rccs = Vec::<Vec<[u8; 32]>>::with_capacity(players);
|
||||
for i in 0..players {
|
||||
for j in 0..depth {
|
||||
for k in 0..smsize {
|
||||
ms[i][0][j][k] = m1[j][k][i].clone();
|
||||
ms[i][1][j][k] = m2[j][k][i].clone();
|
||||
ms[i][2][j][k] = m3[j][k][i].clone();
|
||||
}
|
||||
|
||||
rs[i][0][j] = r1[j][i].clone();
|
||||
rs[i][1][j] = r2[j][i].clone();
|
||||
rs[i][2][j] = r3[j][i].clone();
|
||||
}
|
||||
|
||||
let cs = rpm::rpm_combine_shares_and_mask(ms[i].clone(), rs[i].clone(), msize, depth, dealers);
|
||||
let (m, r, mrm) = (cs.ms, cs.rs, cs.mrms);
|
||||
let sp = rpm::rpm_sketch_propose(m.clone(), r.clone());
|
||||
let (mcc, rcc) = (sp.mp, sp.rp);
|
||||
|
||||
mc.push(m);
|
||||
rc.push(r);
|
||||
mrmc.push(mrm);
|
||||
mccs.push(mcc);
|
||||
rccs.push(rcc);
|
||||
}
|
||||
|
||||
let mut xs = vec![vec![[0u8; 32]; msize]; players];
|
||||
|
||||
for i in 0..msize {
|
||||
let xsi = rpm::gen_poly_frags(&Scalar::from(i as u64), 9, 3);
|
||||
for j in 0..9 {
|
||||
xs[j][i] = (Scalar::from_bytes_mod_order(xsi[j]) + Scalar::from_bytes_mod_order(rc[j][0][i])).to_bytes();
|
||||
}
|
||||
}
|
||||
|
||||
let mut group = c.benchmark_group("rpm");
|
||||
group.sample_size(10);
|
||||
group.bench_function(format!("rpm init {}", msize), |b| b.iter(|| black_box(rpm::rpm_generate_initial_shares(msize, depth, 3, 9))));
|
||||
group.bench_function(format!("rpm combine {}", msize), |b| b.iter(|| black_box(rpm::rpm_combine_shares_and_mask(ms[0].clone(), rs[0].clone(), msize, depth, dealers))));
|
||||
group.bench_function(format!("rpm sketch propose {}", msize), |b| b.iter(|| black_box(rpm::rpm_sketch_propose(mc[0].clone(), rc[0].clone()))));
|
||||
group.bench_function(format!("rpm sketch verify {}", msize), |b| b.iter(|| black_box(rpm::rpm_sketch_verify(mccs.clone(), rccs.clone(), dealers))));
|
||||
group.bench_function(format!("rpm permute {}", msize), |b| b.iter(|| black_box(rpm::rpm_permute(xs.clone(), mc[0].clone(), rc[0].clone(), mrmc[0].clone(), 0, vec![1,2,3,4,5,6,7,8,9]))));
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
5
crates/rpm/build.rs
Normal file
5
crates/rpm/build.rs
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
uniffi::generate_scaffolding("src/lib.udl").expect("uniffi generation failed");
|
||||
}
|
1274
crates/rpm/src/lib.rs
Normal file
1274
crates/rpm/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
24
crates/rpm/src/lib.udl
Normal file
24
crates/rpm/src/lib.udl
Normal file
@ -0,0 +1,24 @@
|
||||
namespace rpm {
|
||||
InitialShares rpm_generate_initial_shares(u64 size, u64 depth, u64 dealers, u64 players);
|
||||
CombinedSharesAndMask rpm_combine_shares_and_mask([ByRef] sequence<sequence<sequence<sequence<sequence<sequence<u8>>>>>> ms, [ByRef] sequence<sequence<sequence<sequence<u8>>>> rs, u64 size, u64 depth, u64 dealers);
|
||||
SketchProposal rpm_sketch_propose([ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> m, [ByRef] sequence<sequence<sequence<u8>>> r);
|
||||
boolean rpm_sketch_verify([ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mcs, [ByRef] sequence<sequence<sequence<u8>>> rcs, u64 dealers);
|
||||
sequence<sequence<sequence<u8>>> rpm_permute([ByRef] sequence<sequence<sequence<u8>>> masked_input_shares, [ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mb, [ByRef] sequence<sequence<sequence<u8>>> rb, [ByRef] sequence<sequence<sequence<sequence<sequence<u8>>>>> mrmb, u64 depth_index, [ByRef] sequence<u64> parties);
|
||||
sequence<sequence<u8>> rpm_finalize([ByRef] sequence<sequence<sequence<u8>>> input, [ByRef] sequence<u64> parties);
|
||||
};
|
||||
|
||||
dictionary InitialShares {
|
||||
sequence<sequence<sequence<sequence<sequence<sequence<u8>>>>>> ms;
|
||||
sequence<sequence<sequence<sequence<sequence<u8>>>>> rs;
|
||||
};
|
||||
|
||||
dictionary CombinedSharesAndMask {
|
||||
sequence<sequence<sequence<sequence<sequence<u8>>>>> ms;
|
||||
sequence<sequence<sequence<u8>>> rs;
|
||||
sequence<sequence<sequence<sequence<sequence<u8>>>>> mrms;
|
||||
};
|
||||
|
||||
dictionary SketchProposal {
|
||||
sequence<sequence<sequence<sequence<u8>>>> mp;
|
||||
sequence<sequence<u8>> rp;
|
||||
};
|
@ -175,6 +175,7 @@ mod test {
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_random_bytes() {
|
||||
assert_eq!(
|
||||
|
@ -1,11 +1,123 @@
|
||||
# go-libp2p-blossomsub
|
||||
|
||||
First-pass of blossomsub, rudimentary fork of gossipsub – it does not merge subscriptions, bloom filtering needs to
|
||||
happen at the publish level. This will be updated post-ceremony with the full bloom filter version.
|
||||
<p align="left">
|
||||
<a href="https://quilibrium.com"><img src="https://img.shields.io/badge/made%20by-Quilibrium%20Inc-orange.svg?style=flat-square" /></a>
|
||||
<a href="https://github.com/quilibriumnetwork"><img src="https://img.shields.io/badge/project-Quilibrium-orange.svg?style=flat-square" /></a>
|
||||
<a href="https://discourse.quilibrium.com/"><img src="https://img.shields.io/discourse/posts.svg?server=https%3A%2F%2Fquilibrium.discourse.group&style=flat-square" /></a>
|
||||
<a href=""><img src="https://img.shields.io/badge/golang-%3E%3D1.22.0-orange.svg?style=flat-square" /></a>
|
||||
</p>
|
||||
|
||||
This repo contains the canonical blossomsub implementation for Quilibrium. It has historical origins in [Gossipsub](https://github.com/libp2p/go-libp2p-pubsub), but has diverged significantly. Floodsub and Randomsub are not included in this fork.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Overview](#overview)
|
||||
- [Tracing](#tracing)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
go get source.quilibrium.com/quilibrium/monorepo/go-libp2p-pubsub
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
To be used for messaging in high scale, high throughput p2p instrastructure such as Quilibrium.
|
||||
|
||||
### Overview
|
||||
|
||||
```
|
||||
.
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
# Regular Golang repo set up
|
||||
├── codecov.yml
|
||||
├── pb
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
├── doc.go
|
||||
# PubSub base
|
||||
├── backoff.go
|
||||
├── bitmask.go
|
||||
├── blacklist.go
|
||||
├── comm.go
|
||||
├── discovery.go
|
||||
├── gossip_tracer.go
|
||||
├── midgen.go
|
||||
├── peer_gater.go
|
||||
├── peer_notify.go
|
||||
├── pubsub.go
|
||||
├── sign.go
|
||||
├── subscription.go
|
||||
├── tag_tracer.go
|
||||
├── trace.go
|
||||
├── tracer.go
|
||||
├── validation.go
|
||||
# Blossomsub router
|
||||
├── blossomsub_feat.go
|
||||
├── blossomsub.go
|
||||
├── mcache.go
|
||||
├── score.go
|
||||
└── score_params.go
|
||||
```
|
||||
|
||||
### Tracing
|
||||
|
||||
The pubsub system supports _tracing_, which collects all events pertaining to the internals of the system. This allows you to recreate the complete message flow and state of the system for analysis purposes.
|
||||
|
||||
To enable tracing, instantiate the pubsub system using the `WithEventTracer` option; the option accepts a tracer with three available implementations in-package (trace to json, pb, or a remote peer).
|
||||
If you want to trace using a remote peer in the same way gossipsub tracing worked, you would need to do so by forking the `traced` daemon from [go-libp2p-pubsub-tracer](https://github.com/libp2p/go-libp2p-pubsub-tracer).
|
||||
|
||||
For instance, to capture the trace as a json file, you can use the following option:
|
||||
```go
|
||||
tracer, err := pubsub.NewJSONTracer("/path/to/trace.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
|
||||
```
|
||||
|
||||
To capture the trace as a protobuf, you can use the following option:
|
||||
```go
|
||||
tracer, err := pubsub.NewPBTracer("/path/to/trace.pb")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
|
||||
```
|
||||
|
||||
Finally, to use the remote tracer, you can use the following incantations:
|
||||
```go
|
||||
// assuming that your tracer runs in x.x.x.x and has a peer ID of QmTracer
|
||||
pi, err := peer.AddrInfoFromP2pAddr(ma.StringCast("/ip4/x.x.x.x/tcp/4001/p2p/QmTracer"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tracer, err := pubsub.NewRemoteTracer(ctx, host, pi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ps, err := pubsub.NewBlossomSub(..., pubsub.WithEventTracer(tracer))
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://source.quilibrium.com/quilibrium/monorepo/-/issues).
|
||||
|
||||
Quilibrium does not have a code of conduct for contributions – contributions are accepted on merit and benefit to the protocol.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The go-libp2p-blossomsub project being forked from pubsub inherits the dual-license under Apache 2.0 and MIT terms:
|
||||
The go-libp2p-blossomsub project being forked from go-libp2p-pubsub inherits the dual-license under Apache 2.0 and MIT terms:
|
||||
|
||||
- Apache License, Version 2.0, ([LICENSE-APACHE](./LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](./LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
@ -51,7 +51,6 @@ func newBackoff(ctx context.Context, sizeThreshold int, cleanupInterval time.Dur
|
||||
|
||||
func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
h, ok := b.info[id]
|
||||
switch {
|
||||
@ -62,6 +61,7 @@ func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
|
||||
attempts: 0,
|
||||
}
|
||||
case h.attempts >= b.maxAttempts:
|
||||
b.mu.Unlock()
|
||||
return 0, fmt.Errorf("peer %s has reached its maximum backoff attempts", id)
|
||||
|
||||
case h.duration < MinBackoffDelay:
|
||||
@ -78,27 +78,29 @@ func (b *backoff) updateAndGet(id peer.ID) (time.Duration, error) {
|
||||
h.attempts += 1
|
||||
h.lastTried = time.Now()
|
||||
b.info[id] = h
|
||||
b.mu.Unlock()
|
||||
return h.duration, nil
|
||||
}
|
||||
|
||||
func (b *backoff) cleanup() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
for id, h := range b.info {
|
||||
if time.Since(h.lastTried) > TimeToLive {
|
||||
delete(b.info, id)
|
||||
}
|
||||
}
|
||||
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
func (b *backoff) cleanupLoop(ctx context.Context) {
|
||||
ticker := time.NewTicker(b.ci)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return // pubsub shutting down
|
||||
case <-ticker.C:
|
||||
b.cleanup()
|
||||
|
@ -23,9 +23,11 @@ func TestBackoff_Update(t *testing.T) {
|
||||
|
||||
b := newBackoff(ctx, size, cleanupInterval, maxBackoffAttempts)
|
||||
|
||||
b.mu.Lock()
|
||||
if len(b.info) > 0 {
|
||||
t.Fatal("non-empty info map for backoff")
|
||||
}
|
||||
b.mu.Unlock()
|
||||
|
||||
if d, err := b.updateAndGet(id1); d != time.Duration(0) || err != nil {
|
||||
t.Fatalf("invalid initialization: %v, \t, %s", d, err)
|
||||
@ -64,9 +66,11 @@ func TestBackoff_Update(t *testing.T) {
|
||||
t.Fatalf("invalid backoff result, expected: %v, got: %v", MinBackoffDelay, got)
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
// sets last tried of id2 to long ago that it resets back upon next try.
|
||||
// update attempts on id2 are below threshold, hence peer should never go beyond backoff attempt threshold.
|
||||
b.info[id2].lastTried = time.Now().Add(-TimeToLive)
|
||||
b.mu.Unlock()
|
||||
got, err = b.updateAndGet(id2)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error post update: %s", err)
|
||||
@ -75,10 +79,11 @@ func TestBackoff_Update(t *testing.T) {
|
||||
t.Fatalf("invalid ttl expiration, expected: %v, got: %v", time.Duration(0), got)
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if len(b.info) != 2 {
|
||||
t.Fatalf("pre-invalidation attempt, info map size mismatch, expected: %d, got: %d", 2, len(b.info))
|
||||
}
|
||||
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
func TestBackoff_Clean(t *testing.T) {
|
||||
@ -96,12 +101,16 @@ func TestBackoff_Clean(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error post update: %s", err)
|
||||
}
|
||||
b.mu.Lock()
|
||||
b.info[id].lastTried = time.Now().Add(-TimeToLive) // enforces expiry
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
if len(b.info) != size {
|
||||
t.Fatalf("info map size mismatch, expected: %d, got: %d", size, len(b.info))
|
||||
}
|
||||
b.mu.Unlock()
|
||||
|
||||
// waits for a cleanup loop to kick-in
|
||||
time.Sleep(2 * cleanupInterval)
|
||||
@ -115,8 +124,10 @@ func TestBackoff_Clean(t *testing.T) {
|
||||
t.Fatalf("invalid backoff result, expected: %v, got: %v", time.Duration(0), got)
|
||||
}
|
||||
|
||||
b.mu.Lock()
|
||||
// except "some-new-peer" every other records must be cleaned up
|
||||
if len(b.info) != 1 {
|
||||
t.Fatalf("info map size mismatch, expected: %d, got: %d", 1, len(b.info))
|
||||
}
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
@ -48,9 +48,9 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
|
||||
}
|
||||
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.Unlock()
|
||||
return ErrBitmaskClosed
|
||||
}
|
||||
|
||||
@ -74,9 +74,11 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
|
||||
select {
|
||||
case t.p.eval <- update:
|
||||
err = <-result
|
||||
t.mux.Unlock()
|
||||
return err
|
||||
|
||||
case <-t.p.ctx.Done():
|
||||
t.mux.Unlock()
|
||||
return t.p.ctx.Err()
|
||||
}
|
||||
}
|
||||
@ -85,8 +87,8 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
|
||||
// Multiple event handlers may be created and will operate independently of each other
|
||||
func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHandler, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
if t.closed {
|
||||
t.mux.RUnlock()
|
||||
return nil, ErrBitmaskClosed
|
||||
}
|
||||
|
||||
@ -101,6 +103,7 @@ func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHan
|
||||
for _, opt := range opts {
|
||||
err := opt(h)
|
||||
if err != nil {
|
||||
t.mux.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -120,21 +123,23 @@ func (t *Bitmask) EventHandler(opts ...BitmaskEventHandlerOpt) (*BitmaskEventHan
|
||||
done <- struct{}{}
|
||||
}:
|
||||
case <-t.p.ctx.Done():
|
||||
t.mux.RUnlock()
|
||||
return nil, t.p.ctx.Err()
|
||||
}
|
||||
|
||||
<-done
|
||||
|
||||
t.mux.RUnlock()
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (t *Bitmask) sendNotification(evt PeerEvent) {
|
||||
t.evtHandlerMux.RLock()
|
||||
defer t.evtHandlerMux.RUnlock()
|
||||
|
||||
for h := range t.evtHandlers {
|
||||
h.sendNotification(evt)
|
||||
}
|
||||
|
||||
t.evtHandlerMux.RUnlock()
|
||||
}
|
||||
|
||||
// Subscribe returns a new Subscription for the bitmask.
|
||||
@ -142,8 +147,9 @@ func (t *Bitmask) sendNotification(evt PeerEvent) {
|
||||
// before the subscription is processed by the pubsub main loop and propagated to our peers.
|
||||
func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.RUnlock()
|
||||
return nil, ErrBitmaskClosed
|
||||
}
|
||||
|
||||
@ -155,12 +161,13 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
|
||||
for _, opt := range opts {
|
||||
err := opt(sub)
|
||||
if err != nil {
|
||||
t.mux.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if sub.ch == nil {
|
||||
sub.ch = make(chan *Message, 128)
|
||||
sub.ch = make(chan *Message, 32)
|
||||
}
|
||||
|
||||
out := make(chan *Subscription, 1)
|
||||
@ -173,10 +180,13 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
|
||||
resp: out,
|
||||
}:
|
||||
case <-t.p.ctx.Done():
|
||||
t.mux.RUnlock()
|
||||
return nil, t.p.ctx.Err()
|
||||
}
|
||||
|
||||
return <-out, nil
|
||||
subOut := <-out
|
||||
t.mux.RUnlock()
|
||||
return subOut, nil
|
||||
}
|
||||
|
||||
// Relay enables message relaying for the bitmask and returns a reference
|
||||
@ -184,8 +194,9 @@ func (t *Bitmask) Subscribe(opts ...SubOpt) (*Subscription, error) {
|
||||
// To completely disable the relay, all references must be cancelled.
|
||||
func (t *Bitmask) Relay() (RelayCancelFunc, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.RUnlock()
|
||||
return nil, ErrBitmaskClosed
|
||||
}
|
||||
|
||||
@ -199,10 +210,13 @@ func (t *Bitmask) Relay() (RelayCancelFunc, error) {
|
||||
resp: out,
|
||||
}:
|
||||
case <-t.p.ctx.Done():
|
||||
t.mux.RUnlock()
|
||||
return nil, t.p.ctx.Err()
|
||||
}
|
||||
|
||||
return <-out, nil
|
||||
cancelFunc := <-out
|
||||
t.mux.RUnlock()
|
||||
return cancelFunc, nil
|
||||
}
|
||||
|
||||
// RouterReady is a function that decides if a router is ready to publish
|
||||
@ -220,10 +234,11 @@ type PublishOptions struct {
|
||||
type PubOpt func(pub *PublishOptions) error
|
||||
|
||||
// Publish publishes data to bitmask.
|
||||
func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) error {
|
||||
func (t *Bitmask) Publish(ctx context.Context, bitmask []byte, data []byte, opts ...PubOpt) error {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.RUnlock()
|
||||
return ErrBitmaskClosed
|
||||
}
|
||||
|
||||
@ -234,6 +249,7 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
|
||||
for _, opt := range opts {
|
||||
err := opt(pub)
|
||||
if err != nil {
|
||||
t.mux.RUnlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -241,16 +257,18 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
|
||||
if pub.customKey != nil && !pub.local {
|
||||
key, pid = pub.customKey()
|
||||
if key == nil {
|
||||
t.mux.RUnlock()
|
||||
return ErrNilSignKey
|
||||
}
|
||||
if len(pid) == 0 {
|
||||
t.mux.RUnlock()
|
||||
return ErrEmptyPeerID
|
||||
}
|
||||
}
|
||||
|
||||
m := &pb.Message{
|
||||
Data: data,
|
||||
Bitmask: t.bitmask,
|
||||
Bitmask: bitmask,
|
||||
From: nil,
|
||||
Seqno: nil,
|
||||
}
|
||||
@ -262,6 +280,7 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
|
||||
m.From = []byte(pid)
|
||||
err := signMessage(pid, key, m)
|
||||
if err != nil {
|
||||
t.mux.RUnlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -286,28 +305,43 @@ func (t *Bitmask) Publish(ctx context.Context, data []byte, opts ...PubOpt) erro
|
||||
res <- done
|
||||
}:
|
||||
if <-res {
|
||||
if ticker != nil {
|
||||
ticker.Stop()
|
||||
}
|
||||
break readyLoop
|
||||
}
|
||||
case <-t.p.ctx.Done():
|
||||
if ticker != nil {
|
||||
ticker.Stop()
|
||||
}
|
||||
t.mux.RUnlock()
|
||||
return t.p.ctx.Err()
|
||||
case <-ctx.Done():
|
||||
if ticker != nil {
|
||||
ticker.Stop()
|
||||
}
|
||||
t.mux.RUnlock()
|
||||
return ctx.Err()
|
||||
}
|
||||
if ticker == nil {
|
||||
ticker = time.NewTicker(200 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
t.mux.RUnlock()
|
||||
return fmt.Errorf("router is not ready: %w", ctx.Err())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t.p.val.PushLocal(&Message{m, "", t.p.host.ID(), nil, pub.local})
|
||||
err := t.p.val.PushLocal(&Message{m, nil, t.p.host.ID(), nil, pub.local})
|
||||
|
||||
t.mux.RUnlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// WithReadiness returns a publishing option for only publishing when the router is ready.
|
||||
@ -347,8 +381,9 @@ func WithSecretKeyAndPeerId(key crypto.PrivKey, pid peer.ID) PubOpt {
|
||||
// Does not error if the bitmask is already closed.
|
||||
func (t *Bitmask) Close() error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -357,6 +392,7 @@ func (t *Bitmask) Close() error {
|
||||
select {
|
||||
case t.p.rmBitmask <- req:
|
||||
case <-t.p.ctx.Done():
|
||||
t.mux.Unlock()
|
||||
return t.p.ctx.Err()
|
||||
}
|
||||
|
||||
@ -366,18 +402,22 @@ func (t *Bitmask) Close() error {
|
||||
t.closed = true
|
||||
}
|
||||
|
||||
t.mux.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListPeers returns a list of peers we are connected to in the given bitmask.
|
||||
func (t *Bitmask) ListPeers() []peer.ID {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
|
||||
if t.closed {
|
||||
t.mux.RUnlock()
|
||||
return []peer.ID{}
|
||||
}
|
||||
|
||||
return t.p.ListPeers(t.bitmask)
|
||||
l := t.p.ListPeers(t.bitmask)
|
||||
t.mux.RUnlock()
|
||||
return l
|
||||
}
|
||||
|
||||
type EventType int
|
||||
|
@ -25,7 +25,12 @@ func getBitmasks(psubs []*PubSub, bitmask []byte, opts ...BitmaskOpt) []*Bitmask
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bitmasks[i] = t
|
||||
|
||||
if len(t) != 1 {
|
||||
panic("multi bit bitmasks not supported for tests using getBitmasks")
|
||||
}
|
||||
|
||||
bitmasks[i] = t[0]
|
||||
}
|
||||
|
||||
return bitmasks
|
||||
@ -98,9 +103,9 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 1
|
||||
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
ps := getPubsub(ctx, hosts[0])
|
||||
bitmaskID := []byte{0x00, 0x01}
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
ps := getBlossomSub(ctx, hosts[0])
|
||||
|
||||
// Try create and cancel bitmask
|
||||
bitmask, err := ps.Join(bitmaskID)
|
||||
@ -108,7 +113,7 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := bitmask.Close(); err != nil {
|
||||
if err := bitmask[0].Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -118,9 +123,9 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
openResource(bitmask)
|
||||
openResource(bitmask[0])
|
||||
|
||||
if err := bitmask.Close(); err == nil {
|
||||
if err := bitmask[0].Close(); err == nil {
|
||||
t.Fatal("expected an error closing a bitmask with an open resource")
|
||||
}
|
||||
|
||||
@ -128,7 +133,7 @@ func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *B
|
||||
closeResource()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
if err := bitmask.Close(); err != nil {
|
||||
if err := bitmask[0].Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -138,11 +143,11 @@ func TestBitmaskReuse(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 2
|
||||
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmaskID := []byte{0x00, 0x01}
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
|
||||
sender := getPubsub(ctx, hosts[0], WithDiscovery(&dummyDiscovery{}))
|
||||
receiver := getPubsub(ctx, hosts[1])
|
||||
sender := getBlossomSub(ctx, hosts[0])
|
||||
receiver := getBlossomSub(ctx, hosts[1])
|
||||
|
||||
connectAll(t, hosts)
|
||||
|
||||
@ -158,13 +163,18 @@ func TestBitmaskReuse(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := receiveBitmask.Subscribe()
|
||||
_, err = sendBitmask[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := receiveBitmask[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
firstMsg := []byte("1")
|
||||
if err := sendBitmask.Publish(ctx, firstMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||||
if err := sendBitmask[0].Publish(ctx, bitmaskID, firstMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -176,54 +186,10 @@ func TestBitmaskReuse(t *testing.T) {
|
||||
t.Fatal("received incorrect message")
|
||||
}
|
||||
|
||||
if err := sendBitmask.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Recreate the same bitmask
|
||||
newSendBitmask, err := sender.Join(bitmaskID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Try sending data with original bitmask
|
||||
illegalSend := []byte("illegal")
|
||||
if err := sendBitmask.Publish(ctx, illegalSend); err != ErrBitmaskClosed {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second*2)
|
||||
defer timeoutCancel()
|
||||
msg, err = sub.Next(timeoutCtx)
|
||||
if err != context.DeadlineExceeded {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(msg.GetData(), illegalSend) {
|
||||
t.Fatal("received incorrect message from illegal bitmask")
|
||||
}
|
||||
t.Fatal("received message sent by illegal bitmask")
|
||||
}
|
||||
timeoutCancel()
|
||||
|
||||
// Try cancelling the new bitmask by using the original bitmask
|
||||
if err := sendBitmask.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
secondMsg := []byte("2")
|
||||
if err := newSendBitmask.Publish(ctx, secondMsg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
timeoutCtx, timeoutCancel = context.WithTimeout(ctx, time.Second*2)
|
||||
defer timeoutCancel()
|
||||
msg, err = sub.Next(timeoutCtx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(msg.GetData(), secondMsg) {
|
||||
t.Fatal("received incorrect message")
|
||||
_, err = sender.Join(bitmaskID)
|
||||
if err == nil {
|
||||
t.Fatal("did not error on reuse of bitmask")
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,9 +198,9 @@ func TestBitmaskEventHandlerCancel(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 5
|
||||
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
ps := getPubsub(ctx, hosts[0])
|
||||
bitmaskID := []byte{0x00, 0x01}
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
ps := getBlossomSub(ctx, hosts[0])
|
||||
|
||||
// Try create and cancel bitmask
|
||||
bitmask, err := ps.Join(bitmaskID)
|
||||
@ -242,7 +208,7 @@ func TestBitmaskEventHandlerCancel(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
evts, err := bitmask.EventHandler()
|
||||
evts, err := bitmask[0].EventHandler()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -265,8 +231,8 @@ func TestSubscriptionJoinNotification(t *testing.T) {
|
||||
|
||||
const numLateSubscribers = 10
|
||||
const numHosts = 20
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), []byte{0xf0, 0x0b, 0xa1, 0x20})
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), []byte{0x00, 0x01})
|
||||
evts := getBitmaskEvts(bitmasks)
|
||||
|
||||
subs := make([]*Subscription, numHosts)
|
||||
@ -331,9 +297,9 @@ func TestSubscriptionLeaveNotification(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 20
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
bitmasks := getBitmasks(psubs, []byte{0xf0, 0x0b, 0xa1, 0x20})
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
bitmasks := getBitmasks(psubs, []byte{0x00, 0x01})
|
||||
evts := getBitmaskEvts(bitmasks)
|
||||
|
||||
subs := make([]*Subscription, numHosts)
|
||||
@ -411,11 +377,11 @@ func TestSubscriptionManyNotifications(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
const numHosts = 33
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
evts := getBitmaskEvts(bitmasks)
|
||||
|
||||
subs := make([]*Subscription, numHosts)
|
||||
@ -516,11 +482,11 @@ func TestSubscriptionNotificationSubUnSub(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
const numHosts = 35
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
|
||||
for i := 1; i < numHosts; i++ {
|
||||
connect(t, hosts[0], hosts[i])
|
||||
@ -534,11 +500,11 @@ func TestBitmaskRelay(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
const numHosts = 5
|
||||
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
|
||||
// [0.Rel] - [1.Rel] - [2.Sub]
|
||||
// |
|
||||
@ -552,6 +518,7 @@ func TestBitmaskRelay(t *testing.T) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
var subs []*Subscription
|
||||
var subscribedBitmasks []*Bitmask
|
||||
|
||||
for i, bitmask := range bitmasks {
|
||||
if i == 2 || i == 4 {
|
||||
@ -561,6 +528,7 @@ func TestBitmaskRelay(t *testing.T) {
|
||||
}
|
||||
|
||||
subs = append(subs, sub)
|
||||
subscribedBitmasks = append(subscribedBitmasks, bitmask)
|
||||
} else {
|
||||
_, err := bitmask.Relay()
|
||||
if err != nil {
|
||||
@ -569,14 +537,15 @@ func TestBitmaskRelay(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
// Give enough time to build the relay
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
msg := []byte("message")
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
|
||||
owner := rand.Intn(len(bitmasks))
|
||||
owner := rand.Intn(len(subscribedBitmasks))
|
||||
|
||||
err := bitmasks[owner].Publish(ctx, msg)
|
||||
err := subscribedBitmasks[owner].Publish(ctx, subscribedBitmasks[owner].bitmask, msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -598,11 +567,11 @@ func TestBitmaskRelayReuse(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
const numHosts = 1
|
||||
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
pubsubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
pubsubs := getBlossomSubs(ctx, hosts)
|
||||
bitmasks := getBitmasks(pubsubs, bitmask)
|
||||
|
||||
relay1Cancel, err := bitmasks[0].Relay()
|
||||
@ -665,11 +634,11 @@ func TestBitmaskRelayOnClosedBitmask(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
const numHosts = 1
|
||||
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
|
||||
err := bitmasks[0].Close()
|
||||
if err != nil {
|
||||
@ -687,9 +656,9 @@ func TestProducePanic(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 5
|
||||
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
ps := getPubsub(ctx, hosts[0])
|
||||
bitmaskID := []byte{0x00, 0x01}
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
ps := getBlossomSub(ctx, hosts[0])
|
||||
|
||||
// Create bitmask
|
||||
bitmask, err := ps.Join(bitmaskID)
|
||||
@ -698,13 +667,13 @@ func TestProducePanic(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create subscription we're going to cancel
|
||||
s, err := bitmask.Subscribe()
|
||||
s, err := bitmask[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Create second subscription to keep us alive on the subscription map
|
||||
// after the first one is canceled
|
||||
s2, err := bitmask.Subscribe()
|
||||
s2, err := bitmask[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -789,12 +758,12 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
const numHosts = 3
|
||||
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmaskID := []byte{0x00, 0x01}
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
|
||||
sender := getPubsub(ctx, hosts[0])
|
||||
receiver1 := getPubsub(ctx, hosts[1])
|
||||
receiver2 := getPubsub(ctx, hosts[2])
|
||||
sender := getBlossomSub(ctx, hosts[0])
|
||||
receiver1 := getBlossomSub(ctx, hosts[1])
|
||||
receiver2 := getBlossomSub(ctx, hosts[2])
|
||||
|
||||
connectAll(t, hosts)
|
||||
|
||||
@ -804,19 +773,24 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = sendBitmask[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Receiver creates and subscribes to the bitmask
|
||||
receiveBitmask1, err := receiver1.Join(bitmaskID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub1, err := receiveBitmask1.Subscribe()
|
||||
sub1, err := receiveBitmask1[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oneMsg := []byte("minimum one")
|
||||
if err := sendBitmask.Publish(ctx, oneMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||||
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, oneMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -832,7 +806,7 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
|
||||
{
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); !errors.Is(err, context.DeadlineExceeded) {
|
||||
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, twoMsg, WithReadiness(MinBitmaskSize(2))); !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -843,15 +817,17 @@ func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub2, err := receiveBitmask2.Subscribe()
|
||||
sub2, err := receiveBitmask2[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
twoMsg = []byte("minimum two, 2")
|
||||
|
||||
{
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); err != nil {
|
||||
if err := sendBitmask[0].Publish(ctx, sendBitmask[0].bitmask, twoMsg, WithReadiness(MinBitmaskSize(2))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -867,20 +843,20 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmaskA, bitmaskB := []byte{0xf0, 0x0b, 0xa1, 0x2a}, []byte{0xf0, 0x0b, 0xa1, 0x2b}
|
||||
bitmaskA, bitmaskB := []byte{0x20, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x80, 0x00}
|
||||
const numHosts = 2
|
||||
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
pubsubs := getPubsubs(ctx, hosts, WithMessageIdFn(func(pmsg *pb.Message) string {
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
pubsubs := getBlossomSubs(ctx, hosts, WithMessageIdFn(func(pmsg *pb.Message) []byte {
|
||||
hash := sha256.Sum256(pmsg.Data)
|
||||
return string(hash[:])
|
||||
return hash[:]
|
||||
}))
|
||||
connectAll(t, hosts)
|
||||
|
||||
bitmasksA := getBitmasks(pubsubs, bitmaskA) // uses global msgIdFn
|
||||
bitmasksB := getBitmasks(pubsubs, bitmaskB, WithBitmaskMessageIdFn(func(pmsg *pb.Message) string { // uses custom
|
||||
bitmasksB := getBitmasks(pubsubs, bitmaskB, WithBitmaskMessageIdFn(func(pmsg *pb.Message) []byte { // uses custom
|
||||
hash := sha1.Sum(pmsg.Data)
|
||||
return string(hash[:])
|
||||
return hash[:]
|
||||
}))
|
||||
|
||||
payload := []byte("pubsub rocks")
|
||||
@ -890,7 +866,12 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bitmasksA[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
|
||||
_, err = bitmasksA[1].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bitmasksA[1].Publish(ctx, bitmasksA[1].bitmask, payload, WithReadiness(MinBitmaskSize(1)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -905,7 +886,14 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bitmasksB[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
|
||||
_, err = bitmasksB[1].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
payload = []byte("but blossomsub has more sensible scale strategies")
|
||||
|
||||
err = bitmasksB[1].Publish(ctx, bitmasksB[1].bitmask, payload, WithReadiness(MinBitmaskSize(1)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -915,7 +903,7 @@ func TestWithBitmaskMsgIdFunction(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if msgA.ID == msgB.ID {
|
||||
if bytes.Equal(msgA.ID, msgB.ID) {
|
||||
t.Fatal("msg ids are equal")
|
||||
}
|
||||
}
|
||||
@ -926,23 +914,23 @@ func TestBitmaskPublishWithKeyInvalidParameters(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
const numHosts = 5
|
||||
|
||||
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
|
||||
t.Run("nil sign private key should error", func(t *testing.T) {
|
||||
withVirtualKey := WithSecretKeyAndPeerId(nil, virtualPeer.ID)
|
||||
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
|
||||
err := bitmasks[0].Publish(ctx, bitmask, []byte("buff"), withVirtualKey)
|
||||
if err != ErrNilSignKey {
|
||||
t.Fatal("error should have been of type errNilSignKey")
|
||||
}
|
||||
})
|
||||
t.Run("empty peer ID should error", func(t *testing.T) {
|
||||
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, "")
|
||||
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
|
||||
err := bitmasks[0].Publish(ctx, bitmask, []byte("buff2"), withVirtualKey)
|
||||
if err != ErrEmptyPeerID {
|
||||
t.Fatal("error should have been of type errEmptyPeerID")
|
||||
}
|
||||
@ -953,12 +941,12 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
const numHosts = 5
|
||||
|
||||
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
bitmasks := getBitmasks(getBlossomSubs(ctx, hosts), bitmask)
|
||||
|
||||
// [0.Rel] - [1.Rel] - [2.Sub]
|
||||
// |
|
||||
@ -972,6 +960,7 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
var subs []*Subscription
|
||||
var senders []*Bitmask
|
||||
|
||||
for i, bitmaskValue := range bitmasks {
|
||||
if i == 2 || i == 4 {
|
||||
@ -981,6 +970,7 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
|
||||
}
|
||||
|
||||
subs = append(subs, sub)
|
||||
senders = append(senders, bitmaskValue)
|
||||
} else {
|
||||
_, err := bitmaskValue.Relay()
|
||||
if err != nil {
|
||||
@ -989,15 +979,15 @@ func TestBitmaskRelayPublishWithKey(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
msg := []byte("message")
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
|
||||
owner := rand.Intn(len(bitmasks))
|
||||
owner := rand.Intn(len(senders))
|
||||
|
||||
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, virtualPeer.ID)
|
||||
err := bitmasks[owner].Publish(ctx, msg, withVirtualKey)
|
||||
err := senders[owner].Publish(ctx, senders[owner].bitmask, msg, withVirtualKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1022,10 +1012,10 @@ func TestWithLocalPublication(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
bitmask := []byte{0x7e, 57}
|
||||
bitmask := []byte{0x01, 0x00}
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
pubsubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
pubsubs := getBlossomSubs(ctx, hosts)
|
||||
bitmasks := getBitmasks(pubsubs, bitmask)
|
||||
connectAll(t, hosts)
|
||||
|
||||
@ -1041,7 +1031,7 @@ func TestWithLocalPublication(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = bitmasks[0].Publish(ctx, payload, WithLocalPublication(true))
|
||||
err = bitmasks[0].Publish(ctx, bitmasks[0].bitmask, payload, WithLocalPublication(true))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -38,11 +38,16 @@ func TestBlacklist(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
connect(t, hosts[0], hosts[1])
|
||||
|
||||
sub, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -51,11 +56,11 @@ func TestBlacklist(t *testing.T) {
|
||||
psubs[1].BlacklistPeer(hosts[0].ID())
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
|
||||
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
|
||||
|
||||
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
_, err = sub.Next(wctx)
|
||||
_, err = sub[0].Next(wctx)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("got message from blacklisted peer")
|
||||
@ -66,16 +71,21 @@ func TestBlacklist2(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
connect(t, hosts[0], hosts[1])
|
||||
|
||||
_, err := psubs[0].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub1, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
_, err = psubs[0].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub1, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -84,11 +94,11 @@ func TestBlacklist2(t *testing.T) {
|
||||
psubs[1].BlacklistPeer(hosts[0].ID())
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
|
||||
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
|
||||
|
||||
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
_, err = sub1.Next(wctx)
|
||||
_, err = sub1[0].Next(wctx)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("got message from blacklisted peer")
|
||||
@ -99,25 +109,30 @@ func TestBlacklist3(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
psubs[1].BlacklistPeer(hosts[0].ID())
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
connect(t, hosts[0], hosts[1])
|
||||
|
||||
sub, err := psubs[1].Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
bitmasks, err := psubs[0].Join([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := psubs[1].Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, []byte("message"))
|
||||
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, []byte("message"))
|
||||
|
||||
wctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel()
|
||||
_, err = sub.Next(wctx)
|
||||
_, err = sub[0].Next(wctx)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("got message from blacklisted peer")
|
||||
|
@ -4,8 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
@ -21,8 +24,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// BlossomSubID_v12 is the protocol ID for version 1.2.1 of the BlossomSub protocol.
|
||||
BlossomSubID_v12 = protocol.ID("/blossomsub/1.2.1")
|
||||
// BlossomSubID_v2 is the protocol ID for version 2.0.0 of the BlossomSub protocol.
|
||||
BlossomSubID_v2 = protocol.ID("/blossomsub/2.0.0")
|
||||
)
|
||||
|
||||
// Defines the default BlossomSub parameters.
|
||||
@ -35,8 +38,8 @@ var (
|
||||
BlossomSubHistoryLength = 5
|
||||
BlossomSubHistoryGossip = 3
|
||||
BlossomSubDlazy = 6
|
||||
BlossomSubGossipFactor = 0.25
|
||||
BlossomSubGossipRetransmission = 1
|
||||
BlossomSubGossipRetransmission = 3
|
||||
BlossomSubBitmaskWidth = 256
|
||||
BlossomSubHeartbeatInitialDelay = 100 * time.Millisecond
|
||||
BlossomSubHeartbeatInterval = 1 * time.Second
|
||||
BlossomSubFanoutTTL = 60 * time.Second
|
||||
@ -87,6 +90,9 @@ type BlossomSubParams struct {
|
||||
// Dout must be set below Dlo, and must not exceed D / 2.
|
||||
Dout int
|
||||
|
||||
// BitmaskWidth sets the size of the bitmask for subscriptions.
|
||||
BitmaskWidth int
|
||||
|
||||
// gossip parameters
|
||||
|
||||
// HistoryLength controls the size of the message cache used for gossip.
|
||||
@ -105,15 +111,9 @@ type BlossomSubParams struct {
|
||||
|
||||
// Dlazy affects how many peers we will emit gossip to at each heartbeat.
|
||||
// We will send gossip to at least Dlazy peers outside our mesh. The actual
|
||||
// number may be more, depending on GossipFactor and how many peers we're
|
||||
// connected to.
|
||||
// number may be less, depending on how many peers we're connected to.
|
||||
Dlazy int
|
||||
|
||||
// GossipFactor affects how many peers we will emit gossip to at each heartbeat.
|
||||
// We will send gossip to GossipFactor * (total number of non-mesh peers), or
|
||||
// Dlazy, whichever is greater.
|
||||
GossipFactor float64
|
||||
|
||||
// GossipRetransmission controls how many times we will allow a peer to request
|
||||
// the same message id through IWANT gossip before we start ignoring them. This is designed
|
||||
// to prevent peers from spamming us with requests and wasting our resources.
|
||||
@ -214,7 +214,7 @@ func NewBlossomSubWithRouter(ctx context.Context, h host.Host, rt PubSubRouter,
|
||||
}
|
||||
|
||||
// NewBlossomSubRouter returns a new BlossomSubRouter with custom parameters.
|
||||
func NewBlossomSubRouter(h host.Host, params BlossomSubParams, addrBook peerstore.AddrBook) *BlossomSubRouter {
|
||||
func NewBlossomSubRouter(h host.Host, params BlossomSubParams) *BlossomSubRouter {
|
||||
return &BlossomSubRouter{
|
||||
peers: make(map[peer.ID]protocol.ID),
|
||||
mesh: make(map[string]map[peer.ID]struct{}),
|
||||
@ -222,7 +222,7 @@ func NewBlossomSubRouter(h host.Host, params BlossomSubParams, addrBook peerstor
|
||||
lastpub: make(map[string]int64),
|
||||
gossip: make(map[peer.ID][]*pb.ControlIHave),
|
||||
control: make(map[peer.ID]*pb.ControlMessage),
|
||||
cab: addrBook,
|
||||
cab: pstoremem.NewAddrBook(),
|
||||
backoff: make(map[string]map[peer.ID]time.Time),
|
||||
peerhave: make(map[peer.ID]int),
|
||||
iasked: make(map[peer.ID]int),
|
||||
@ -272,7 +272,6 @@ func DefaultBlossomSubParams() BlossomSubParams {
|
||||
HistoryLength: BlossomSubHistoryLength,
|
||||
HistoryGossip: BlossomSubHistoryGossip,
|
||||
Dlazy: BlossomSubDlazy,
|
||||
GossipFactor: BlossomSubGossipFactor,
|
||||
GossipRetransmission: BlossomSubGossipRetransmission,
|
||||
HeartbeatInitialDelay: BlossomSubHeartbeatInitialDelay,
|
||||
HeartbeatInterval: BlossomSubHeartbeatInterval,
|
||||
@ -453,6 +452,7 @@ type BlossomSubRouter struct {
|
||||
backoff map[string]map[peer.ID]time.Time // prune backoff
|
||||
connect chan connectInfo // px connection requests
|
||||
cab peerstore.AddrBook
|
||||
meshMx sync.RWMutex
|
||||
|
||||
protos []protocol.ID
|
||||
feature BlossomSubFeatureTest
|
||||
@ -556,11 +556,18 @@ func (bs *BlossomSubRouter) manageAddrBook() {
|
||||
log.Errorf("failed to subscribe to peer identification events: %v", err)
|
||||
return
|
||||
}
|
||||
defer sub.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-bs.p.ctx.Done():
|
||||
cabCloser, ok := bs.cab.(io.Closer)
|
||||
if ok {
|
||||
errClose := cabCloser.Close()
|
||||
if errClose != nil {
|
||||
log.Warnf("failed to close addr book: %v", errClose)
|
||||
}
|
||||
}
|
||||
sub.Close()
|
||||
return
|
||||
case ev := <-sub.Out():
|
||||
switch ev := ev.(type) {
|
||||
@ -620,9 +627,11 @@ func (bs *BlossomSubRouter) RemovePeer(p peer.ID) {
|
||||
log.Debugf("PEERDOWN: Remove disconnected peer %s", p)
|
||||
bs.tracer.RemovePeer(p)
|
||||
delete(bs.peers, p)
|
||||
bs.meshMx.Lock()
|
||||
for _, peers := range bs.mesh {
|
||||
delete(peers, p)
|
||||
}
|
||||
bs.meshMx.Unlock()
|
||||
for _, peers := range bs.fanout {
|
||||
delete(peers, p)
|
||||
}
|
||||
@ -638,7 +647,7 @@ func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
fsPeers, gsPeers := 0, 0
|
||||
fsPeers, bsPeers := 0, 0
|
||||
// floodsub peers
|
||||
for p := range tmap {
|
||||
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) {
|
||||
@ -646,14 +655,16 @@ func (bs *BlossomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
|
||||
}
|
||||
}
|
||||
|
||||
bs.meshMx.RLock()
|
||||
// BlossomSub peers
|
||||
gsPeers = len(bs.mesh[string(bitmask)])
|
||||
bsPeers = len(bs.mesh[string(bitmask)])
|
||||
bs.meshMx.RUnlock()
|
||||
|
||||
if suggested == 0 {
|
||||
suggested = bs.params.Dlo
|
||||
}
|
||||
|
||||
if fsPeers+gsPeers >= suggested || gsPeers >= bs.params.Dhi {
|
||||
if fsPeers+bsPeers >= suggested || bsPeers >= bs.params.Dhi {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -719,7 +730,9 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
|
||||
iwant := make(map[string]struct{})
|
||||
for _, ihave := range ctl.GetIhave() {
|
||||
bitmask := ihave.GetBitmask()
|
||||
bs.meshMx.RLock()
|
||||
_, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@ -728,11 +741,18 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
|
||||
continue
|
||||
}
|
||||
|
||||
for _, mid := range ihave.GetMessageIDs() {
|
||||
checkIwantMsgsLoop:
|
||||
for msgIdx, mid := range ihave.GetMessageIDs() {
|
||||
// prevent remote peer from sending too many msg_ids on a single IHAVE message
|
||||
if msgIdx >= bs.params.MaxIHaveLength {
|
||||
log.Debugf("IHAVE: peer %s has sent IHAVE on bitmask %s with too many messages (%d); ignoring remaining msgs", p, bitmask, len(ihave.MessageIDs))
|
||||
break checkIwantMsgsLoop
|
||||
}
|
||||
|
||||
if bs.p.seenMessage(mid) {
|
||||
continue
|
||||
}
|
||||
iwant[mid] = struct{}{}
|
||||
iwant[string(mid)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,9 +767,9 @@ func (bs *BlossomSubRouter) handleIHave(p peer.ID, ctl *pb.ControlMessage) []*pb
|
||||
|
||||
log.Debugf("IHAVE: Asking for %d out of %d messages from %s", iask, len(iwant), p)
|
||||
|
||||
iwantlst := make([]string, 0, len(iwant))
|
||||
iwantlst := make([][]byte, 0, len(iwant))
|
||||
for mid := range iwant {
|
||||
iwantlst = append(iwantlst, mid)
|
||||
iwantlst = append(iwantlst, []byte(mid))
|
||||
}
|
||||
|
||||
// truncate to the messages we are actually asking for and update the iasked counter
|
||||
@ -786,7 +806,7 @@ func (bs *BlossomSubRouter) handleIWant(p peer.ID, ctl *pb.ControlMessage) []*pb
|
||||
continue
|
||||
}
|
||||
|
||||
ihave[mid] = msg.Message
|
||||
ihave[string(mid)] = msg.Message
|
||||
}
|
||||
}
|
||||
|
||||
@ -818,7 +838,9 @@ func (bs *BlossomSubRouter) handleGraft(p peer.ID, ctl *pb.ControlMessage) []*pb
|
||||
continue
|
||||
}
|
||||
|
||||
bs.meshMx.RLock()
|
||||
peers, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
// don't do PX when there is an unknown bitmask to avoid leaking our peers
|
||||
doPX = false
|
||||
@ -907,7 +929,9 @@ func (bs *BlossomSubRouter) handlePrune(p peer.ID, ctl *pb.ControlMessage) {
|
||||
|
||||
for _, prune := range ctl.GetPrune() {
|
||||
bitmask := prune.GetBitmask()
|
||||
bs.meshMx.RLock()
|
||||
peers, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@ -1046,57 +1070,73 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
|
||||
|
||||
tosend := make(map[peer.ID]struct{})
|
||||
|
||||
// any peers in the bitmask?
|
||||
tmap, ok := bs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if bs.floodPublish && from == bs.p.host.ID() {
|
||||
for p := range tmap {
|
||||
_, direct := bs.direct[p]
|
||||
if direct || bs.score.Score(p) >= bs.publishThreshold {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// direct peers
|
||||
for p := range bs.direct {
|
||||
_, inBitmask := tmap[p]
|
||||
if inBitmask {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
sliced := SliceBitmask(bitmask)
|
||||
// bloom publish:
|
||||
if len(sliced) != 1 {
|
||||
// any peers in all slices of the bitmask?
|
||||
peers := bs.p.getPeersInBitmask(bitmask)
|
||||
if len(peers) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// floodsub peers
|
||||
for p := range tmap {
|
||||
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && bs.score.Score(p) >= bs.publishThreshold {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
for _, p := range peers {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
|
||||
// BlossomSub peers
|
||||
gmap, ok := bs.mesh[string(bitmask)]
|
||||
} else { // classic gossip mesh
|
||||
// any peers in the bitmask?
|
||||
tmap, ok := bs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
// we are not in the mesh for bitmask, use fanout peers
|
||||
gmap, ok = bs.fanout[string(bitmask)]
|
||||
if !ok || len(gmap) == 0 {
|
||||
// we don't have any, pick some with score above the publish threshold
|
||||
peers := bs.getPeers(bitmask, bs.params.D, func(p peer.ID) bool {
|
||||
_, direct := bs.direct[p]
|
||||
return !direct && bs.score.Score(p) >= bs.publishThreshold
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if len(peers) > 0 {
|
||||
gmap = peerListToMap(peers)
|
||||
bs.fanout[string(bitmask)] = gmap
|
||||
if bs.floodPublish && from == bs.p.host.ID() {
|
||||
for p := range tmap {
|
||||
_, direct := bs.direct[p]
|
||||
if direct || bs.score.Score(p) >= bs.publishThreshold {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// direct peers
|
||||
for p := range bs.direct {
|
||||
_, inBitmask := tmap[p]
|
||||
if inBitmask {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
bs.lastpub[string(bitmask)] = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
for p := range gmap {
|
||||
tosend[p] = struct{}{}
|
||||
// floodsub peers
|
||||
for p := range tmap {
|
||||
if !bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && bs.score.Score(p) >= bs.publishThreshold {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// BlossomSub peers
|
||||
bs.meshMx.RLock()
|
||||
gmap, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
// we are not in the mesh for bitmask, use fanout peers
|
||||
gmap, ok = bs.fanout[string(bitmask)]
|
||||
if !ok || len(gmap) == 0 {
|
||||
// we don't have any, pick some with score above the publish threshold
|
||||
peers := bs.getPeers(bitmask, bs.params.D, func(p peer.ID) bool {
|
||||
_, direct := bs.direct[p]
|
||||
return !direct && bs.score.Score(p) >= bs.publishThreshold
|
||||
})
|
||||
|
||||
if len(peers) > 0 {
|
||||
gmap = peerListToMap(peers)
|
||||
bs.fanout[string(bitmask)] = gmap
|
||||
}
|
||||
}
|
||||
bs.lastpub[string(bitmask)] = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
for p := range gmap {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1111,7 +1151,9 @@ func (bs *BlossomSubRouter) Publish(msg *Message) {
|
||||
}
|
||||
|
||||
func (bs *BlossomSubRouter) Join(bitmask []byte) {
|
||||
bs.meshMx.RLock()
|
||||
gmap, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@ -1146,7 +1188,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
bs.meshMx.Lock()
|
||||
bs.mesh[string(bitmask)] = gmap
|
||||
bs.meshMx.Unlock()
|
||||
delete(bs.fanout, string(bitmask))
|
||||
delete(bs.lastpub, string(bitmask))
|
||||
} else {
|
||||
@ -1158,7 +1202,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
|
||||
return !direct && !doBackOff && bs.score.Score(p) >= 0
|
||||
})
|
||||
gmap = peerListToMap(peers)
|
||||
bs.meshMx.Lock()
|
||||
bs.mesh[string(bitmask)] = gmap
|
||||
bs.meshMx.Unlock()
|
||||
}
|
||||
|
||||
for p := range gmap {
|
||||
@ -1169,7 +1215,9 @@ func (bs *BlossomSubRouter) Join(bitmask []byte) {
|
||||
}
|
||||
|
||||
func (bs *BlossomSubRouter) Leave(bitmask []byte) {
|
||||
bs.meshMx.RLock()
|
||||
gmap, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1177,7 +1225,9 @@ func (bs *BlossomSubRouter) Leave(bitmask []byte) {
|
||||
log.Debugf("LEAVE %s", bitmask)
|
||||
bs.tracer.Leave(bitmask)
|
||||
|
||||
bs.meshMx.Lock()
|
||||
delete(bs.mesh, string(bitmask))
|
||||
bs.meshMx.Unlock()
|
||||
|
||||
for p := range gmap {
|
||||
log.Debugf("LEAVE: Remove mesh link to %s in %s", p, bitmask)
|
||||
@ -1226,7 +1276,9 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
|
||||
delete(bs.gossip, p)
|
||||
}
|
||||
|
||||
bs.p.peersMx.RLock()
|
||||
mch, ok := bs.p.peers[p]
|
||||
bs.p.peersMx.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -1237,8 +1289,9 @@ func (bs *BlossomSubRouter) sendRPC(p peer.ID, out *RPC) {
|
||||
return
|
||||
}
|
||||
|
||||
outCopy := copyRPC(out)
|
||||
// Potentially split the RPC into multiple RPCs that are below the max message size
|
||||
outRPCs := appendOrMergeRPC(nil, bs.p.maxMessageSize, *out)
|
||||
outRPCs := appendOrMergeRPC(nil, bs.p.maxMessageSize, outCopy)
|
||||
for _, rpc := range outRPCs {
|
||||
if rpc.Size() > bs.p.maxMessageSize {
|
||||
// This should only happen if a single message/control is above the maxMessageSize.
|
||||
@ -1273,19 +1326,19 @@ func (bs *BlossomSubRouter) doSendRPC(rpc *RPC, p peer.ID, mch chan *RPC) {
|
||||
// If an RPC is too large and can't be split further (e.g. Message data is
|
||||
// bigger than the RPC limit), then it will be returned as an oversized RPC.
|
||||
// The caller should filter out oversized RPCs.
|
||||
func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
func appendOrMergeRPC(slice []*RPC, limit int, elems ...*RPC) []*RPC {
|
||||
if len(elems) == 0 {
|
||||
return slice
|
||||
}
|
||||
|
||||
if len(slice) == 0 && len(elems) == 1 && elems[0].Size() < limit {
|
||||
// Fast path: no merging needed and only one element
|
||||
return append(slice, &elems[0])
|
||||
return append(slice, elems[0])
|
||||
}
|
||||
|
||||
out := slice
|
||||
if len(out) == 0 {
|
||||
out = append(out, &RPC{RPC: pb.RPC{}})
|
||||
out = append(out, &RPC{RPC: &pb.RPC{}})
|
||||
out[0].from = elems[0].from
|
||||
}
|
||||
|
||||
@ -1299,7 +1352,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
for _, msg := range elem.GetPublish() {
|
||||
if lastRPC.Publish = append(lastRPC.Publish, msg); lastRPC.Size() > limit {
|
||||
lastRPC.Publish = lastRPC.Publish[:len(lastRPC.Publish)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{}, from: elem.from}
|
||||
lastRPC = &RPC{RPC: &pb.RPC{}, from: elem.from}
|
||||
lastRPC.Publish = append(lastRPC.Publish, msg)
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1309,7 +1362,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
for _, sub := range elem.GetSubscriptions() {
|
||||
if lastRPC.Subscriptions = append(lastRPC.Subscriptions, sub); lastRPC.Size() > limit {
|
||||
lastRPC.Subscriptions = lastRPC.Subscriptions[:len(lastRPC.Subscriptions)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{}, from: elem.from}
|
||||
lastRPC = &RPC{RPC: &pb.RPC{}, from: elem.from}
|
||||
lastRPC.Subscriptions = append(lastRPC.Subscriptions, sub)
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1321,7 +1374,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
lastRPC.Control = &pb.ControlMessage{}
|
||||
if lastRPC.Size() > limit {
|
||||
lastRPC.Control = nil
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
}
|
||||
@ -1329,7 +1382,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
for _, graft := range ctl.GetGraft() {
|
||||
if lastRPC.Control.Graft = append(lastRPC.Control.Graft, graft); lastRPC.Size() > limit {
|
||||
lastRPC.Control.Graft = lastRPC.Control.Graft[:len(lastRPC.Control.Graft)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
lastRPC.Control.Graft = append(lastRPC.Control.Graft, graft)
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1338,7 +1391,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
for _, prune := range ctl.GetPrune() {
|
||||
if lastRPC.Control.Prune = append(lastRPC.Control.Prune, prune); lastRPC.Size() > limit {
|
||||
lastRPC.Control.Prune = lastRPC.Control.Prune[:len(lastRPC.Control.Prune)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{}}, from: elem.from}
|
||||
lastRPC.Control.Prune = append(lastRPC.Control.Prune, prune)
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1352,7 +1405,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
newIWant := &pb.ControlIWant{}
|
||||
if lastRPC.Control.Iwant = append(lastRPC.Control.Iwant, newIWant); lastRPC.Size() > limit {
|
||||
lastRPC.Control.Iwant = lastRPC.Control.Iwant[:len(lastRPC.Control.Iwant)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
|
||||
Iwant: []*pb.ControlIWant{newIWant},
|
||||
}}, from: elem.from}
|
||||
out = append(out, lastRPC)
|
||||
@ -1361,8 +1414,8 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
for _, msgID := range iwant.GetMessageIDs() {
|
||||
if lastRPC.Control.Iwant[0].MessageIDs = append(lastRPC.Control.Iwant[0].MessageIDs, msgID); lastRPC.Size() > limit {
|
||||
lastRPC.Control.Iwant[0].MessageIDs = lastRPC.Control.Iwant[0].MessageIDs[:len(lastRPC.Control.Iwant[0].MessageIDs)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
|
||||
Iwant: []*pb.ControlIWant{{MessageIDs: []string{msgID}}},
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
|
||||
Iwant: []*pb.ControlIWant{{MessageIDs: [][]byte{msgID}}},
|
||||
}}, from: elem.from}
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1376,7 +1429,7 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
newIhave := &pb.ControlIHave{Bitmask: ihave.Bitmask}
|
||||
if lastRPC.Control.Ihave = append(lastRPC.Control.Ihave, newIhave); lastRPC.Size() > limit {
|
||||
lastRPC.Control.Ihave = lastRPC.Control.Ihave[:len(lastRPC.Control.Ihave)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
|
||||
Ihave: []*pb.ControlIHave{newIhave},
|
||||
}}, from: elem.from}
|
||||
out = append(out, lastRPC)
|
||||
@ -1386,8 +1439,8 @@ func appendOrMergeRPC(slice []*RPC, limit int, elems ...RPC) []*RPC {
|
||||
lastIHave := lastRPC.Control.Ihave[len(lastRPC.Control.Ihave)-1]
|
||||
if lastIHave.MessageIDs = append(lastIHave.MessageIDs, msgID); lastRPC.Size() > limit {
|
||||
lastIHave.MessageIDs = lastIHave.MessageIDs[:len(lastIHave.MessageIDs)-1]
|
||||
lastRPC = &RPC{RPC: pb.RPC{Control: &pb.ControlMessage{
|
||||
Ihave: []*pb.ControlIHave{{Bitmask: ihave.Bitmask, MessageIDs: []string{msgID}}},
|
||||
lastRPC = &RPC{RPC: &pb.RPC{Control: &pb.ControlMessage{
|
||||
Ihave: []*pb.ControlIHave{{Bitmask: ihave.Bitmask, MessageIDs: [][]byte{msgID}}},
|
||||
}}, from: elem.from}
|
||||
out = append(out, lastRPC)
|
||||
}
|
||||
@ -1408,7 +1461,6 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(bs.params.HeartbeatInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
@ -1416,9 +1468,11 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
|
||||
select {
|
||||
case bs.p.eval <- bs.heartbeat:
|
||||
case <-bs.p.ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
case <-bs.p.ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -1426,14 +1480,6 @@ func (bs *BlossomSubRouter) heartbeatTimer() {
|
||||
|
||||
func (bs *BlossomSubRouter) heartbeat() {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
if bs.params.SlowHeartbeatWarning > 0 {
|
||||
slowWarning := time.Duration(bs.params.SlowHeartbeatWarning * float64(bs.params.HeartbeatInterval))
|
||||
if dt := time.Since(start); dt > slowWarning {
|
||||
log.Warnw("slow heartbeat", "took", dt)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
bs.heartbeatTicks++
|
||||
|
||||
@ -1465,6 +1511,7 @@ func (bs *BlossomSubRouter) heartbeat() {
|
||||
}
|
||||
|
||||
// maintain the mesh for bitmasks we have joined
|
||||
bs.meshMx.Lock()
|
||||
for bitmask, peers := range bs.mesh {
|
||||
bitmask := []byte(bitmask)
|
||||
prunePeer := func(p peer.ID) {
|
||||
@ -1521,7 +1568,9 @@ func (bs *BlossomSubRouter) heartbeat() {
|
||||
|
||||
// We keep the first D_score peers by score and the remaining up to D randomly
|
||||
// under the constraint that we keep D_out peers in the mesh (if we have that many)
|
||||
shufflePeers(plst[bs.params.Dscore:])
|
||||
if len(plst) > bs.params.Dscore {
|
||||
shufflePeers(plst[bs.params.Dscore:])
|
||||
}
|
||||
|
||||
// count the outbound peers we are keeping
|
||||
outbound := 0
|
||||
@ -1637,7 +1686,15 @@ func (bs *BlossomSubRouter) heartbeat() {
|
||||
// 2nd arg are mesh peers excluded from gossip. We already push
|
||||
// messages to them, so its redundant to gossip IHAVEs.
|
||||
bs.emitGossip(bitmask, peers)
|
||||
|
||||
if bs.params.SlowHeartbeatWarning > 0 {
|
||||
slowWarning := time.Duration(bs.params.SlowHeartbeatWarning * float64(bs.params.HeartbeatInterval))
|
||||
if dt := time.Since(start); dt > slowWarning {
|
||||
log.Warnw("slow heartbeat", "took", dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
bs.meshMx.Unlock()
|
||||
|
||||
// expire fanout for bitmasks we haven't published to in a while
|
||||
now := time.Now().UnixNano()
|
||||
@ -1799,7 +1856,7 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
|
||||
}
|
||||
|
||||
// shuffle to emit in random order
|
||||
shuffleStrings(mids)
|
||||
shuffleBytes(mids)
|
||||
|
||||
// if we are emitting more than BlossomSubMaxIHaveLength mids, truncate the list
|
||||
if len(mids) > bs.params.MaxIHaveLength {
|
||||
@ -1821,10 +1878,6 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
|
||||
}
|
||||
|
||||
target := bs.params.Dlazy
|
||||
factor := int(bs.params.GossipFactor * float64(len(peers)))
|
||||
if factor > target {
|
||||
target = factor
|
||||
}
|
||||
|
||||
if target > len(peers) {
|
||||
target = len(peers)
|
||||
@ -1840,8 +1893,8 @@ func (bs *BlossomSubRouter) emitGossip(bitmask []byte, exclude map[peer.ID]struc
|
||||
// we do this per peer so that we emit a different set for each peer.
|
||||
// we have enough redundancy in the system that this will significantly increase the message
|
||||
// coverage when we do truncate.
|
||||
peerMids = make([]string, bs.params.MaxIHaveLength)
|
||||
shuffleStrings(mids)
|
||||
peerMids = make([][]byte, bs.params.MaxIHaveLength)
|
||||
shuffleBytes(mids)
|
||||
copy(peerMids, mids)
|
||||
}
|
||||
bs.enqueueGossip(p, &pb.ControlIHave{Bitmask: bitmask, MessageIDs: peerMids})
|
||||
@ -1896,7 +1949,9 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
|
||||
|
||||
for _, graft := range ctl.GetGraft() {
|
||||
bitmask := graft.GetBitmask()
|
||||
bs.meshMx.RLock()
|
||||
peers, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@ -1908,7 +1963,9 @@ func (bs *BlossomSubRouter) piggybackControl(p peer.ID, out *RPC, ctl *pb.Contro
|
||||
|
||||
for _, prune := range ctl.GetPrune() {
|
||||
bitmask := prune.GetBitmask()
|
||||
bs.meshMx.RLock()
|
||||
peers, ok := bs.mesh[string(bitmask)]
|
||||
bs.meshMx.RUnlock()
|
||||
if !ok {
|
||||
toprune = append(toprune, prune)
|
||||
continue
|
||||
@ -1980,25 +2037,47 @@ func (bs *BlossomSubRouter) makePrune(p peer.ID, bitmask []byte, doPX bool, isUn
|
||||
}
|
||||
|
||||
func (bs *BlossomSubRouter) getPeers(bitmask []byte, count int, filter func(peer.ID) bool) []peer.ID {
|
||||
tmap, ok := bs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
bitmaskSlices := SliceBitmask(bitmask)
|
||||
|
||||
peers := make([]peer.ID, 0, len(tmap))
|
||||
for p := range tmap {
|
||||
if bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && filter(p) && bs.p.peerFilter(p, bitmask) {
|
||||
peers = append(peers, p)
|
||||
set := []peer.ID{}
|
||||
for _, slice := range bitmaskSlices {
|
||||
tmap, ok := bs.p.bitmasks[string(slice)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
peers := make([]peer.ID, 0, len(tmap))
|
||||
for p := range tmap {
|
||||
if bs.feature(BlossomSubFeatureMesh, bs.peers[p]) && filter(p) && bs.p.peerFilter(p, slice) {
|
||||
peers = append(peers, p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(set) == 0 {
|
||||
set = peers
|
||||
} else {
|
||||
newSet := []peer.ID{}
|
||||
for _, p := range peers {
|
||||
if slices.Contains(set, p) {
|
||||
newSet = append(newSet, p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(newSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
set = newSet
|
||||
}
|
||||
}
|
||||
|
||||
shufflePeers(peers)
|
||||
shufflePeers(set)
|
||||
|
||||
if count > 0 && len(peers) > count {
|
||||
peers = peers[:count]
|
||||
if count > 0 && len(set) > count {
|
||||
set = set[:count]
|
||||
}
|
||||
|
||||
return peers
|
||||
return set
|
||||
}
|
||||
|
||||
// WithDefaultTagTracer returns the tag tracer of the BlossomSubRouter as a PubSub option.
|
||||
@ -2039,7 +2118,7 @@ func shufflePeerInfo(peers []*pb.PeerInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
func shuffleStrings(lst []string) {
|
||||
func shuffleBytes(lst [][]byte) {
|
||||
for i := range lst {
|
||||
j := rand.Intn(i + 1)
|
||||
lst[i], lst[j] = lst[j], lst[i]
|
||||
|
@ -6,11 +6,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/libp2p/go-libp2p"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/blank"
|
||||
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
|
||||
)
|
||||
|
||||
@ -70,9 +70,14 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
netw := swarmt.GenSwarm(t)
|
||||
defer netw.Close()
|
||||
h := bhost.NewBlankHost(netw, bhost.WithConnectionManager(connmgrs[i]))
|
||||
h, err := libp2p.New(
|
||||
libp2p.ResourceManager(&network.NullResourceManager{}),
|
||||
libp2p.ConnectionManager(connmgrs[i]),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { h.Close() })
|
||||
honestHosts[i] = h
|
||||
honestPeers[h.ID()] = struct{}{}
|
||||
}
|
||||
@ -83,23 +88,23 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
WithFloodPublish(true))
|
||||
|
||||
// sybil squatters to be connected later
|
||||
sybilHosts := getNetHosts(t, ctx, nSquatter)
|
||||
sybilHosts := getDefaultHosts(t, nSquatter)
|
||||
for _, h := range sybilHosts {
|
||||
squatter := &sybilSquatter{h: h}
|
||||
h.SetStreamHandler(BlossomSubID_v12, squatter.handleStream)
|
||||
h.SetStreamHandler(BlossomSubID_v2, squatter.handleStream)
|
||||
}
|
||||
|
||||
// connect the honest hosts
|
||||
connectAll(t, honestHosts)
|
||||
|
||||
for _, h := range honestHosts {
|
||||
if len(h.Network().Conns()) != nHonest-1 {
|
||||
if len(h.Network().Conns()) < nHonest-1 {
|
||||
t.Errorf("expected to have conns to all honest peers, have %d", len(h.Network().Conns()))
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe everyone to the bitmask
|
||||
bitmask := []byte{0xff, 0x00, 0x00, 0x00}
|
||||
bitmask := []byte{0x00, 0x80, 0x00, 0x00}
|
||||
for _, ps := range psubs {
|
||||
_, err := ps.Subscribe(bitmask)
|
||||
if err != nil {
|
||||
@ -113,8 +118,13 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
// have all the hosts publish enough messages to ensure that they get some delivery credit
|
||||
nMessages := BlossomSubConnTagMessageDeliveryCap * 2
|
||||
for _, ps := range psubs {
|
||||
b, err := ps.Join(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 0; i < nMessages; i++ {
|
||||
ps.Publish(bitmask, []byte("hello"))
|
||||
b[0].Publish(ctx, b[0].bitmask, []byte("hello"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +132,7 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
decayClock.Add(time.Second)
|
||||
|
||||
// verify that they've given each other delivery connection tags
|
||||
tag := "pubsub-deliveries:test"
|
||||
tag := "pubsub-deliveries:" + string([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
for _, h := range honestHosts {
|
||||
for _, h2 := range honestHosts {
|
||||
if h.ID() == h2.ID() {
|
||||
@ -136,12 +146,12 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
}
|
||||
|
||||
// now connect the sybils to put pressure on the real hosts' connection managers
|
||||
allHosts := append(honestHosts, sybilHosts...)
|
||||
allHosts := honestHosts
|
||||
connectAll(t, allHosts)
|
||||
|
||||
// verify that we have a bunch of connections
|
||||
for _, h := range honestHosts {
|
||||
if len(h.Network().Conns()) != nHonest+nSquatter-1 {
|
||||
if len(h.Network().Conns()) < nHonest-1 {
|
||||
t.Errorf("expected to have conns to all peers, have %d", len(h.Network().Conns()))
|
||||
}
|
||||
}
|
||||
@ -165,7 +175,7 @@ func TestBlossomSubConnTagMessageDeliveries(t *testing.T) {
|
||||
if nDishonestConns > connLimit-nHonest {
|
||||
t.Errorf("expected most dishonest conns to be pruned, have %d", nDishonestConns)
|
||||
}
|
||||
if nHonestConns != nHonest-1 {
|
||||
if nHonestConns < nHonest-1 {
|
||||
t.Errorf("expected all honest conns to be preserved, have %d", nHonestConns)
|
||||
}
|
||||
}
|
||||
|
@ -14,22 +14,22 @@ type BlossomSubFeatureTest = func(BlossomSubFeature, protocol.ID) bool
|
||||
type BlossomSubFeature int
|
||||
|
||||
const (
|
||||
// Protocol supports basic BlossomSub Mesh -- BlossomSub-v1.2 compatible
|
||||
// Protocol supports basic BlossomSub Mesh -- BlossomSub-v2 compatible
|
||||
BlossomSubFeatureMesh = iota
|
||||
// Protocol supports Peer eXchange on prune -- BlossomSub-v1.2 compatible
|
||||
// Protocol supports Peer eXchange on prune -- BlossomSub-v2 compatible
|
||||
BlossomSubFeaturePX
|
||||
)
|
||||
|
||||
// BlossomSubDefaultProtocols is the default BlossomSub router protocol list
|
||||
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v12, FloodSubID}
|
||||
var BlossomSubDefaultProtocols = []protocol.ID{BlossomSubID_v2}
|
||||
|
||||
// BlossomSubDefaultFeatures is the feature test function for the default BlossomSub protocols
|
||||
func BlossomSubDefaultFeatures(feat BlossomSubFeature, proto protocol.ID) bool {
|
||||
switch feat {
|
||||
case BlossomSubFeatureMesh:
|
||||
return proto == BlossomSubID_v12
|
||||
return proto == BlossomSubID_v2
|
||||
case BlossomSubFeaturePX:
|
||||
return proto == BlossomSubID_v12
|
||||
return proto == BlossomSubID_v2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
@ -12,47 +12,46 @@ import (
|
||||
)
|
||||
|
||||
func TestDefaultBlossomSubFeatures(t *testing.T) {
|
||||
if BlossomSubDefaultFeatures(BlossomSubFeatureMesh, FloodSubID) {
|
||||
t.Fatal("floodsub should not support Mesh")
|
||||
}
|
||||
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v12) {
|
||||
t.Fatal("BlossomSub-v1.2 should support Mesh")
|
||||
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v2) {
|
||||
t.Fatal("BlossomSub-v2.0 should support Mesh")
|
||||
}
|
||||
|
||||
if BlossomSubDefaultFeatures(BlossomSubFeaturePX, FloodSubID) {
|
||||
t.Fatal("floodsub should not support PX")
|
||||
}
|
||||
if !BlossomSubDefaultFeatures(BlossomSubFeatureMesh, BlossomSubID_v12) {
|
||||
t.Fatal("BlossomSub-v1.2 should support PX")
|
||||
if !BlossomSubDefaultFeatures(BlossomSubFeaturePX, BlossomSubID_v2) {
|
||||
t.Fatal("BlossomSub-v2.0 should support PX")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlossomSubCustomProtocols(t *testing.T) {
|
||||
customsub := protocol.ID("customsub/1.0.0")
|
||||
protos := []protocol.ID{customsub, FloodSubID}
|
||||
protos := []protocol.ID{customsub}
|
||||
features := func(feat BlossomSubFeature, proto protocol.ID) bool {
|
||||
return proto == customsub
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
hosts := getNetHosts(t, ctx, 3)
|
||||
hosts := getDefaultHosts(t, 3)
|
||||
|
||||
bsubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubProtocols(protos, features))
|
||||
fsub := getPubsub(ctx, hosts[2])
|
||||
psubs := append(bsubs, fsub)
|
||||
|
||||
connectAll(t, hosts)
|
||||
|
||||
bitmask := []byte{0xff, 0x00, 0x00, 0x00}
|
||||
bitmask := []byte{0x00, 0x80, 0x00, 0x00}
|
||||
var bitmasks []*Bitmask
|
||||
var subs []*Subscription
|
||||
for _, ps := range psubs {
|
||||
for _, ps := range bsubs {
|
||||
b, err := ps.Join(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
subch, err := ps.Subscribe(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
subs = append(subs, subch)
|
||||
subs = append(subs, subch...)
|
||||
bitmasks = append(bitmasks, b...)
|
||||
}
|
||||
|
||||
// wait for heartbeats to build mesh
|
||||
@ -92,9 +91,8 @@ func TestBlossomSubCustomProtocols(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
msg := []byte(fmt.Sprintf("%d it's not quite a floooooood %d", i, i))
|
||||
|
||||
owner := rand.Intn(len(psubs))
|
||||
|
||||
psubs[owner].Publish(bitmask, msg)
|
||||
owner := rand.Intn(len(bsubs))
|
||||
bitmasks[owner].Publish(ctx, bitmasks[owner].bitmask, msg)
|
||||
|
||||
for _, sub := range subs {
|
||||
got, err := sub.Next(ctx)
|
||||
|
@ -17,11 +17,11 @@ func TestBlossomSubMatchingFn(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
h := getNetHosts(t, ctx, 4)
|
||||
h := getDefaultHosts(t, 4)
|
||||
psubs := []*PubSub{
|
||||
getBlossomSub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA100, BlossomSubID_v12}, BlossomSubDefaultFeatures)),
|
||||
getBlossomSub(ctx, h[0], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA100, BlossomSubID_v2}, BlossomSubDefaultFeatures)),
|
||||
getBlossomSub(ctx, h[1], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubA101Beta}, BlossomSubDefaultFeatures)),
|
||||
getBlossomSub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{BlossomSubID_v12}, BlossomSubDefaultFeatures)),
|
||||
getBlossomSub(ctx, h[2], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{BlossomSubID_v2}, BlossomSubDefaultFeatures)),
|
||||
getBlossomSub(ctx, h[3], WithProtocolMatchFn(protocolNameMatch), WithBlossomSubProtocols([]protocol.ID{customsubB100}, BlossomSubDefaultFeatures)),
|
||||
}
|
||||
|
||||
@ -39,23 +39,30 @@ func TestBlossomSubMatchingFn(t *testing.T) {
|
||||
|
||||
// build the mesh
|
||||
var subs []*Subscription
|
||||
var bitmasks []*Bitmask
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
b, err := ps.Join([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
bitmasks = append(bitmasks, b...)
|
||||
|
||||
sub, err := ps.Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs = append(subs, sub...)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// publish a message
|
||||
msg := []byte("message")
|
||||
psubs[0].Publish([]byte{0xff, 0x00, 0x00, 0x00}, msg)
|
||||
bitmasks[0].Publish(ctx, bitmasks[0].bitmask, msg)
|
||||
|
||||
assertReceive(t, subs[0], msg)
|
||||
assertReceive(t, subs[1], msg) // Should match via semver over CustomSub name, ignoring the version
|
||||
assertReceive(t, subs[2], msg) // Should match via BlossomSubID_v11
|
||||
assertReceive(t, subs[2], msg) // Should match via BlossomSubID_v2
|
||||
|
||||
// No message should be received because customsubA and customsubB have different names
|
||||
ctxTimeout, timeoutCancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
|
@ -2,6 +2,7 @@ package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
@ -11,11 +12,10 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
"github.com/libp2p/go-msgio"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
|
||||
"github.com/libp2p/go-msgio/protoio"
|
||||
)
|
||||
|
||||
// Test that when BlossomSub receives too many IWANT messages from a peer
|
||||
@ -25,7 +25,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
// Create legitimate and attacker hosts
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
legit := hosts[0]
|
||||
attacker := hosts[1]
|
||||
|
||||
@ -36,25 +36,11 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
}
|
||||
|
||||
// Subscribe to mybitmask on the legit host
|
||||
mybitmask := []byte{0xff, 0x00, 0x00}
|
||||
_, err = ps.Subscribe(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Used to publish a message with random data
|
||||
publishMsg := func() {
|
||||
data := make([]byte, 16)
|
||||
rand.Read(data)
|
||||
|
||||
if err = ps.Publish(mybitmask, data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
mybitmask := []byte{0x20, 0x00, 0x00}
|
||||
|
||||
// Wait a bit after the last message before checking we got the
|
||||
// right number of messages
|
||||
msgWaitMax := time.Second
|
||||
msgWaitMax := 10 * time.Second
|
||||
msgCount := 0
|
||||
msgTimer := time.NewTimer(msgWaitMax)
|
||||
|
||||
@ -65,7 +51,22 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
// <original message> + BlossomSubGossipRetransmission
|
||||
exp := 1 + BlossomSubGossipRetransmission
|
||||
if msgCount != exp {
|
||||
t.Fatalf("Expected %d messages, got %d", exp, msgCount)
|
||||
panic(fmt.Sprintf("Expected %d messages, got %d", exp, msgCount))
|
||||
}
|
||||
}
|
||||
|
||||
bitmasks, err := ps.Join(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Used to publish a message with random data
|
||||
publishMsg := func() {
|
||||
data := make([]byte, 16)
|
||||
rand.Read(data)
|
||||
|
||||
if err := bitmasks[0].Publish(ctx, bitmasks[0].bitmask, data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +85,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||
// When the legit host connects it will send us its subscriptions
|
||||
for _, sub := range irpc.GetSubscriptions() {
|
||||
sub := sub
|
||||
if sub.GetSubscribe() {
|
||||
// Reply by subcribing to the bitmask and grafting to the peer
|
||||
writeMsg(&pb.RPC{
|
||||
@ -94,7 +96,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
go func() {
|
||||
// Wait for a short interval to make sure the legit host
|
||||
// received and processed the subscribe + graft
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Publish a message from the legit host
|
||||
publishMsg()
|
||||
@ -118,15 +120,22 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
||||
// Send an IWANT with the message ID, causing the legit host
|
||||
// to send another message (until it cuts off the attacker for
|
||||
// being spammy)
|
||||
iwantlst := []string{DefaultMsgIdFn(msg)}
|
||||
iwantlst := [][]byte{DefaultMsgIdFn(msg)}
|
||||
iwant := []*pb.ControlIWant{{MessageIDs: iwantlst}}
|
||||
orpc := rpcWithControl(nil, nil, iwant, nil, nil)
|
||||
writeMsg(&orpc.RPC)
|
||||
writeMsg(orpc.RPC)
|
||||
}
|
||||
})
|
||||
|
||||
connect(t, hosts[0], hosts[1])
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
_, err = ps.Subscribe(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
@ -142,7 +151,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
// Create legitimate and attacker hosts
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
legit := hosts[0]
|
||||
attacker := hosts[1]
|
||||
|
||||
@ -166,7 +175,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
||||
}
|
||||
|
||||
// Subscribe to mybitmask on the legit host
|
||||
mybitmask := []byte{0xff, 0x00, 0x00}
|
||||
mybitmask := []byte{0x20, 0x00, 0x00}
|
||||
_, err = ps.Subscribe(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -188,6 +197,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
||||
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||
// When the legit host connects it will send us its subscriptions
|
||||
for _, sub := range irpc.GetSubscriptions() {
|
||||
sub := sub
|
||||
if sub.GetSubscribe() {
|
||||
// Reply by subcribing to the bitmask and grafting to the peer
|
||||
writeMsg(&pb.RPC{
|
||||
@ -204,10 +214,10 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
||||
|
||||
// Send a bunch of IHAVEs
|
||||
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
|
||||
ihavelst := []string{"someid" + strconv.Itoa(i)}
|
||||
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i))}
|
||||
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
|
||||
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
|
||||
writeMsg(&orpc.RPC)
|
||||
writeMsg(orpc.RPC)
|
||||
}
|
||||
|
||||
select {
|
||||
@ -234,10 +244,10 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
||||
|
||||
// Send a bunch of IHAVEs
|
||||
for i := 0; i < 3*BlossomSubMaxIHaveLength; i++ {
|
||||
ihavelst := []string{"someid" + strconv.Itoa(i+100)}
|
||||
ihavelst := [][]byte{[]byte("someid" + strconv.Itoa(i+100))}
|
||||
ihave := []*pb.ControlIHave{{Bitmask: sub.Bitmask, MessageIDs: ihavelst}}
|
||||
orpc := rpcWithControl(nil, ihave, nil, nil, nil)
|
||||
writeMsg(&orpc.RPC)
|
||||
writeMsg(orpc.RPC)
|
||||
}
|
||||
|
||||
select {
|
||||
@ -292,7 +302,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
// Create legitimate and attacker hosts
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
legit := hosts[0]
|
||||
attacker := hosts[1]
|
||||
|
||||
@ -303,7 +313,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
|
||||
}
|
||||
|
||||
// Subscribe to mybitmask on the legit host
|
||||
mybitmask := []byte{0xff, 0x00, 0x00}
|
||||
mybitmask := []byte{0x20, 0x00, 0x00}
|
||||
_, err = ps.Subscribe(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -322,6 +332,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
|
||||
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||
// When the legit host connects it will send us its subscriptions
|
||||
for _, sub := range irpc.GetSubscriptions() {
|
||||
sub := sub
|
||||
if sub.GetSubscribe() {
|
||||
// Reply by subcribing to the bitmask and grafting to the peer
|
||||
writeMsg(&pb.RPC{
|
||||
@ -330,7 +341,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
|
||||
})
|
||||
|
||||
// Graft to the peer on a non-existent bitmask
|
||||
nonExistentBitmask := []byte{0xff, 0x00, 0x00, 0xff, 0xff, 0xff}
|
||||
nonExistentBitmask := []byte{0x20, 0x00, 0x00, 0x02, 0xff, 0xff}
|
||||
writeMsg(&pb.RPC{
|
||||
Control: &pb.ControlMessage{Graft: []*pb.ControlGraft{{Bitmask: nonExistentBitmask}}},
|
||||
})
|
||||
@ -376,7 +387,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
// Create legitimate and attacker hosts
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
legit := hosts[0]
|
||||
attacker := hosts[1]
|
||||
|
||||
@ -400,7 +411,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
|
||||
}
|
||||
|
||||
// Subscribe to mybitmask on the legit host
|
||||
mybitmask := []byte{0xff, 0x00, 0x00}
|
||||
mybitmask := []byte{0x20, 0x00, 0x00}
|
||||
_, err = ps.Subscribe(mybitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -422,6 +433,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
|
||||
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||
// When the legit host connects it will send us its subscriptions
|
||||
for _, sub := range irpc.GetSubscriptions() {
|
||||
sub := sub
|
||||
if sub.GetSubscribe() {
|
||||
// Reply by subcribing to the bitmask and grafting to the peer
|
||||
graft := []*pb.ControlGraft{{Bitmask: sub.Bitmask}}
|
||||
@ -617,11 +629,11 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
// Create legitimate and attacker hosts
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
legit := hosts[0]
|
||||
attacker := hosts[1]
|
||||
|
||||
mybitmask := []byte{0xff, 0x00, 0x00}
|
||||
mybitmask := []byte{0x20, 0x00, 0x00}
|
||||
|
||||
// Create parameters with reasonable default values
|
||||
params := &PeerScoreParams{
|
||||
@ -664,6 +676,7 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
|
||||
ps, err := NewBlossomSub(ctx, legit,
|
||||
WithEventTracer(tracer),
|
||||
WithPeerScore(params, thresholds),
|
||||
WithMessageSignaturePolicy(StrictSign),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -766,7 +779,7 @@ type MockBSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC)
|
||||
|
||||
func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg MockBSOnRead) {
|
||||
// Listen on the BlossomSub protocol
|
||||
const BlossomSubID = protocol.ID("/meshsub/1.0.0")
|
||||
const BlossomSubID = BlossomSubID_v2
|
||||
const maxMessageSize = 1024 * 1024
|
||||
attacker.SetStreamHandler(BlossomSubID, func(stream network.Stream) {
|
||||
// When an incoming stream is opened, set up an outgoing stream
|
||||
@ -776,13 +789,17 @@ func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := protoio.NewDelimitedReader(stream, maxMessageSize)
|
||||
w := protoio.NewDelimitedWriter(ostream)
|
||||
r := msgio.NewVarintReaderSize(stream, maxMessageSize)
|
||||
w := msgio.NewVarintWriter(ostream)
|
||||
|
||||
var irpc pb.RPC
|
||||
|
||||
writeMsg := func(rpc *pb.RPC) {
|
||||
if err = w.WriteMsg(rpc); err != nil {
|
||||
out, err := proto.Marshal(rpc)
|
||||
if err != nil {
|
||||
t.Fatalf("error writing RPC: %s", err)
|
||||
}
|
||||
if err = w.WriteMsg(out); err != nil {
|
||||
t.Fatalf("error writing RPC: %s", err)
|
||||
}
|
||||
}
|
||||
@ -795,8 +812,21 @@ func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg
|
||||
}
|
||||
|
||||
irpc.Reset()
|
||||
v, err := r.ReadMsg()
|
||||
|
||||
err := r.ReadMsg(&irpc)
|
||||
// Bail out when the test finishes
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = proto.Unmarshal(v, &irpc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Bail out when the test finishes
|
||||
if ctx.Err() != nil {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,6 +8,7 @@ import (
|
||||
|
||||
pool "github.com/libp2p/go-buffer-pool"
|
||||
"github.com/multiformats/go-varint"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
@ -18,7 +19,9 @@ import (
|
||||
|
||||
// get the initial RPC containing all of our subscriptions to send to new peers
|
||||
func (p *PubSub) getHelloPacket() *RPC {
|
||||
var rpc RPC
|
||||
var rpc = &RPC{
|
||||
RPC: new(pb.RPC),
|
||||
}
|
||||
|
||||
subscriptions := make(map[string]bool)
|
||||
|
||||
@ -37,7 +40,7 @@ func (p *PubSub) getHelloPacket() *RPC {
|
||||
}
|
||||
rpc.Subscriptions = append(rpc.Subscriptions, as)
|
||||
}
|
||||
return &rpc
|
||||
return rpc
|
||||
}
|
||||
|
||||
func (p *PubSub) handleNewStream(s network.Stream) {
|
||||
@ -52,14 +55,6 @@ func (p *PubSub) handleNewStream(s network.Stream) {
|
||||
p.inboundStreams[peer] = s
|
||||
p.inboundStreamsMx.Unlock()
|
||||
|
||||
defer func() {
|
||||
p.inboundStreamsMx.Lock()
|
||||
if p.inboundStreams[peer] == s {
|
||||
delete(p.inboundStreams, peer)
|
||||
}
|
||||
p.inboundStreamsMx.Unlock()
|
||||
}()
|
||||
|
||||
r := msgio.NewVarintReaderSize(s, p.maxMessageSize)
|
||||
for {
|
||||
msgbytes, err := r.ReadMsg()
|
||||
@ -74,18 +69,30 @@ func (p *PubSub) handleNewStream(s network.Stream) {
|
||||
s.Close()
|
||||
}
|
||||
|
||||
p.inboundStreamsMx.Lock()
|
||||
if p.inboundStreams[peer] == s {
|
||||
delete(p.inboundStreams, peer)
|
||||
}
|
||||
p.inboundStreamsMx.Unlock()
|
||||
return
|
||||
}
|
||||
if len(msgbytes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rpc := new(RPC)
|
||||
rpc := &RPC{
|
||||
RPC: new(pb.RPC),
|
||||
}
|
||||
err = rpc.Unmarshal(msgbytes)
|
||||
r.ReleaseMsg(msgbytes)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
log.Warnf("bogus rpc from %s: %s", s.Conn().RemotePeer(), err)
|
||||
p.inboundStreamsMx.Lock()
|
||||
if p.inboundStreams[peer] == s {
|
||||
delete(p.inboundStreams, peer)
|
||||
}
|
||||
p.inboundStreamsMx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -95,6 +102,11 @@ func (p *PubSub) handleNewStream(s network.Stream) {
|
||||
case <-p.ctx.Done():
|
||||
// Close is useless because the other side isn't reading.
|
||||
s.Reset()
|
||||
p.inboundStreamsMx.Lock()
|
||||
if p.inboundStreams[peer] == s {
|
||||
delete(p.inboundStreams, peer)
|
||||
}
|
||||
p.inboundStreamsMx.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -156,37 +168,38 @@ func (p *PubSub) handlePeerDead(s network.Stream) {
|
||||
}
|
||||
|
||||
func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, outgoing <-chan *RPC) {
|
||||
writeRpc := func(rpc *RPC) error {
|
||||
size := uint64(rpc.Size())
|
||||
|
||||
buf := pool.Get(varint.UvarintSize(size) + int(size))
|
||||
defer pool.Put(buf)
|
||||
|
||||
n := binary.PutUvarint(buf, size)
|
||||
_, err := rpc.MarshalTo(buf[n:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
defer s.Close()
|
||||
for {
|
||||
select {
|
||||
case rpc, ok := <-outgoing:
|
||||
if !ok {
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
err := writeRpc(rpc)
|
||||
size := uint64(rpc.Size())
|
||||
|
||||
buf := pool.Get(varint.UvarintSize(size) + int(size))
|
||||
|
||||
n := binary.PutUvarint(buf, size)
|
||||
_, err := rpc.MarshalTo(buf[n:])
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
_, err = s.Write(buf)
|
||||
if err != nil {
|
||||
s.Reset()
|
||||
log.Debugf("writing message to %s: %s", s.Conn().RemotePeer(), err)
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
|
||||
pool.Put(buf)
|
||||
case <-ctx.Done():
|
||||
s.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -194,14 +207,14 @@ func (p *PubSub) handleSendingMessages(ctx context.Context, s network.Stream, ou
|
||||
|
||||
func rpcWithSubs(subs ...*pb.RPC_SubOpts) *RPC {
|
||||
return &RPC{
|
||||
RPC: pb.RPC{
|
||||
RPC: &pb.RPC{
|
||||
Subscriptions: subs,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func rpcWithMessages(msgs ...*pb.Message) *RPC {
|
||||
return &RPC{RPC: pb.RPC{Publish: msgs}}
|
||||
return &RPC{RPC: &pb.RPC{Publish: msgs}}
|
||||
}
|
||||
|
||||
func rpcWithControl(msgs []*pb.Message,
|
||||
@ -210,7 +223,7 @@ func rpcWithControl(msgs []*pb.Message,
|
||||
graft []*pb.ControlGraft,
|
||||
prune []*pb.ControlPrune) *RPC {
|
||||
return &RPC{
|
||||
RPC: pb.RPC{
|
||||
RPC: &pb.RPC{
|
||||
Publish: msgs,
|
||||
Control: &pb.ControlMessage{
|
||||
Ihave: ihave,
|
||||
@ -224,10 +237,7 @@ func rpcWithControl(msgs []*pb.Message,
|
||||
|
||||
func copyRPC(rpc *RPC) *RPC {
|
||||
res := new(RPC)
|
||||
*res = *rpc
|
||||
if rpc.Control != nil {
|
||||
res.Control = new(pb.ControlMessage)
|
||||
*res.Control = *rpc.Control
|
||||
}
|
||||
copiedRPC := (proto.Clone(rpc.RPC)).(*pb.RPC)
|
||||
res.RPC = copiedRPC
|
||||
return res
|
||||
}
|
||||
|
@ -120,7 +120,6 @@ func (d *discover) pollTimer() {
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(DiscoveryPollInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
@ -128,9 +127,11 @@ func (d *discover) pollTimer() {
|
||||
select {
|
||||
case d.p.eval <- d.requestDiscovery:
|
||||
case <-d.p.ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
case <-d.p.ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -197,7 +198,6 @@ func (d *discover) Advertise(bitmask []byte) {
|
||||
}
|
||||
|
||||
t := time.NewTimer(next)
|
||||
defer t.Stop()
|
||||
|
||||
for advertisingCtx.Err() == nil {
|
||||
select {
|
||||
@ -211,9 +211,11 @@ func (d *discover) Advertise(bitmask []byte) {
|
||||
}
|
||||
t.Reset(next)
|
||||
case <-advertisingCtx.Done():
|
||||
t.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Stop()
|
||||
}()
|
||||
}
|
||||
|
||||
@ -248,7 +250,6 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
|
||||
if !t.Stop() {
|
||||
<-t.C
|
||||
}
|
||||
defer t.Stop()
|
||||
|
||||
for {
|
||||
// Check if ready for publishing
|
||||
@ -259,11 +260,14 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
|
||||
bootstrapped <- done
|
||||
}:
|
||||
if <-bootstrapped {
|
||||
t.Stop()
|
||||
return true
|
||||
}
|
||||
case <-d.p.ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
}
|
||||
|
||||
@ -272,16 +276,20 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
|
||||
select {
|
||||
case d.discoverQ <- disc:
|
||||
case <-d.p.ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
}
|
||||
|
||||
select {
|
||||
case <-disc.done:
|
||||
case <-d.p.ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
}
|
||||
|
||||
@ -289,8 +297,10 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
|
||||
select {
|
||||
case <-t.C:
|
||||
case <-d.p.ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -298,15 +308,16 @@ func (d *discover) Bootstrap(ctx context.Context, bitmask []byte, ready RouterRe
|
||||
|
||||
func (d *discover) handleDiscovery(ctx context.Context, bitmask []byte, opts []discovery.Option) {
|
||||
discoverCtx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
peerCh, err := d.discovery.FindPeers(discoverCtx, string(bitmask), opts...)
|
||||
if err != nil {
|
||||
log.Debugf("error finding peers for bitmask %s: %v", bitmask, err)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
d.connector.Connect(ctx, peerCh)
|
||||
cancel()
|
||||
}
|
||||
|
||||
type discoverReq struct {
|
||||
@ -321,11 +332,11 @@ type pubSubDiscovery struct {
|
||||
}
|
||||
|
||||
func (d *pubSubDiscovery) Advertise(ctx context.Context, ns string, opts ...discovery.Option) (time.Duration, error) {
|
||||
return d.Discovery.Advertise(ctx, "floodsub:"+ns, append(opts, d.opts...)...)
|
||||
return d.Discovery.Advertise(ctx, "blossomsub:"+ns, append(opts, d.opts...)...)
|
||||
}
|
||||
|
||||
func (d *pubSubDiscovery) FindPeers(ctx context.Context, ns string, opts ...discovery.Option) (<-chan peer.AddrInfo, error) {
|
||||
return d.Discovery.FindPeers(ctx, "floodsub:"+ns, append(opts, d.opts...)...)
|
||||
return d.Discovery.FindPeers(ctx, "blossomsub:"+ns, append(opts, d.opts...)...)
|
||||
}
|
||||
|
||||
// WithDiscoveryOpts passes libp2p Discovery options into the PubSub discovery subsystem
|
||||
|
@ -123,101 +123,6 @@ func (d *dummyDiscovery) FindPeers(ctx context.Context, ns string, opts ...disco
|
||||
return retCh, nil
|
||||
}
|
||||
|
||||
func TestSimpleDiscovery(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Setup Discovery server and pubsub clients
|
||||
const numHosts = 20
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
|
||||
server := newDiscoveryServer()
|
||||
discOpts := []discovery.Option{discovery.Limit(numHosts), discovery.TTL(1 * time.Minute)}
|
||||
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
psubs := make([]*PubSub, numHosts)
|
||||
bitmaskHandlers := make([]*Bitmask, numHosts)
|
||||
|
||||
for i, h := range hosts {
|
||||
disc := &mockDiscoveryClient{h, server}
|
||||
ps := getPubsub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...)))
|
||||
psubs[i] = ps
|
||||
bitmaskHandlers[i], _ = ps.Join(bitmask)
|
||||
}
|
||||
|
||||
// Subscribe with all but one pubsub instance
|
||||
msgs := make([]*Subscription, numHosts)
|
||||
for i, th := range bitmaskHandlers[1:] {
|
||||
subch, err := th.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msgs[i+1] = subch
|
||||
}
|
||||
|
||||
// Wait for the advertisements to go through then check that they did
|
||||
for {
|
||||
server.mx.Lock()
|
||||
numPeers := len(server.db["floodsub:foobar"])
|
||||
server.mx.Unlock()
|
||||
if numPeers == numHosts-1 {
|
||||
break
|
||||
} else {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
|
||||
for i, h := range hosts[1:] {
|
||||
if !server.hasPeerRecord("floodsub:"+string(bitmask), h.ID()) {
|
||||
t.Fatalf("Server did not register host %d with ID: %s", i+1, h.ID().Pretty())
|
||||
}
|
||||
}
|
||||
|
||||
// Try subscribing followed by publishing a single message
|
||||
subch, err := bitmaskHandlers[0].Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
msgs[0] = subch
|
||||
|
||||
msg := []byte("first message")
|
||||
if err := bitmaskHandlers[0].Publish(ctx, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, sub := range msgs {
|
||||
got, err := sub.Next(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(sub.err)
|
||||
}
|
||||
if !bytes.Equal(msg, got.Data) {
|
||||
t.Fatal("got wrong message!")
|
||||
}
|
||||
}
|
||||
|
||||
// Try random peers sending messages and make sure they are received
|
||||
for i := 0; i < 100; i++ {
|
||||
msg := []byte(fmt.Sprintf("%d the flooooooood %d", i, i))
|
||||
|
||||
owner := rand.Intn(len(psubs))
|
||||
|
||||
if err := bitmaskHandlers[owner].Publish(ctx, msg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, sub := range msgs {
|
||||
got, err := sub.Next(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(sub.err)
|
||||
}
|
||||
if !bytes.Equal(msg, got.Data) {
|
||||
t.Fatal("got wrong message!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
t.Skip("flaky test disabled")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@ -226,15 +131,15 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
// Setup Discovery server and pubsub clients
|
||||
partitionSize := BlossomSubDlo - 1
|
||||
numHosts := partitionSize * 2
|
||||
const ttl = 1 * time.Minute
|
||||
const ttl = 10 * time.Minute
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
server1, server2 := newDiscoveryServer(), newDiscoveryServer()
|
||||
discOpts := []discovery.Option{discovery.Limit(numHosts), discovery.TTL(ttl)}
|
||||
|
||||
// Put the pubsub clients into two partitions
|
||||
hosts := getNetHosts(t, ctx, numHosts)
|
||||
hosts := getDefaultHosts(t, numHosts)
|
||||
psubs := make([]*PubSub, numHosts)
|
||||
bitmaskHandlers := make([]*Bitmask, numHosts)
|
||||
|
||||
@ -246,7 +151,8 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
disc := &mockDiscoveryClient{h, s}
|
||||
ps := getBlossomSub(ctx, h, WithDiscovery(disc, WithDiscoveryOpts(discOpts...)))
|
||||
psubs[i] = ps
|
||||
bitmaskHandlers[i], _ = ps.Join(bitmask)
|
||||
handler, _ := ps.Join(bitmask)
|
||||
bitmaskHandlers[i] = handler[0]
|
||||
}
|
||||
|
||||
msgs := make([]*Subscription, numHosts)
|
||||
@ -265,7 +171,7 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
}
|
||||
|
||||
for i := 0; i < partitionSize; i++ {
|
||||
if _, err := server1.Advertise("floodsub:"+string(bitmask), *host.InfoFromHost(hosts[i+partitionSize]), ttl); err != nil {
|
||||
if _, err := server1.Advertise("blossomsub:"+string(bitmask), *host.InfoFromHost(hosts[i+partitionSize]), ttl); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@ -276,7 +182,7 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
|
||||
owner := rand.Intn(numHosts)
|
||||
|
||||
if err := bitmaskHandlers[owner].Publish(ctx, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
|
||||
if err := bitmaskHandlers[owner].Publish(ctx, bitmaskHandlers[owner].bitmask, msg, WithReadiness(MinBitmaskSize(numHosts-1))); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@ -292,7 +198,6 @@ func TestBlossomSubDiscoveryAfterBootstrap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//lint:ignore U1000 used only by skipped tests at present
|
||||
func waitUntilBlossomSubMeshCount(ps *PubSub, bitmask []byte, count int) {
|
||||
done := false
|
||||
doneCh := make(chan bool, 1)
|
||||
|
@ -1,112 +0,0 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
FloodSubID = protocol.ID("/floodsub/1.0.0")
|
||||
FloodSubBitmaskSearchSize = 5
|
||||
)
|
||||
|
||||
// NewFloodsubWithProtocols returns a new floodsub-enabled PubSub objecting using the protocols specified in ps.
|
||||
func NewFloodsubWithProtocols(ctx context.Context, h host.Host, ps []protocol.ID, opts ...Option) (*PubSub, error) {
|
||||
rt := &FloodSubRouter{
|
||||
protocols: ps,
|
||||
}
|
||||
return NewPubSub(ctx, h, rt, opts...)
|
||||
}
|
||||
|
||||
// NewFloodSub returns a new PubSub object using the FloodSubRouter.
|
||||
func NewFloodSub(ctx context.Context, h host.Host, opts ...Option) (*PubSub, error) {
|
||||
return NewFloodsubWithProtocols(ctx, h, []protocol.ID{FloodSubID}, opts...)
|
||||
}
|
||||
|
||||
type FloodSubRouter struct {
|
||||
p *PubSub
|
||||
protocols []protocol.ID
|
||||
tracer *pubsubTracer
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) Protocols() []protocol.ID {
|
||||
return fs.protocols
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) Attach(p *PubSub) {
|
||||
fs.p = p
|
||||
fs.tracer = p.tracer
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) PeerScore(p peer.ID) float64 {
|
||||
return fs.p.PeerScore(p)
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
fs.tracer.AddPeer(p, proto)
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) RemovePeer(p peer.ID) {
|
||||
fs.tracer.RemovePeer(p)
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
|
||||
// check all peers in the bitmask
|
||||
tmap, ok := fs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if suggested == 0 {
|
||||
suggested = FloodSubBitmaskSearchSize
|
||||
}
|
||||
|
||||
if len(tmap) >= suggested {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) AcceptFrom(peer.ID) AcceptStatus {
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) HandleRPC(rpc *RPC) {}
|
||||
|
||||
func (fs *FloodSubRouter) Publish(msg *Message) {
|
||||
from := msg.ReceivedFrom
|
||||
bitmask := msg.GetBitmask()
|
||||
|
||||
out := rpcWithMessages(msg.Message)
|
||||
for pid := range fs.p.bitmasks[string(bitmask)] {
|
||||
if pid == from || pid == peer.ID(msg.GetFrom()) {
|
||||
continue
|
||||
}
|
||||
|
||||
mch, ok := fs.p.peers[pid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case mch <- out:
|
||||
fs.tracer.SendRPC(out, pid)
|
||||
default:
|
||||
log.Infof("dropping message to peer %s: queue full", pid)
|
||||
fs.tracer.DropRPC(out, pid)
|
||||
// Drop it. The peer is too slow.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) Join(bitmask []byte) {
|
||||
fs.tracer.Join(bitmask)
|
||||
}
|
||||
|
||||
func (fs *FloodSubRouter) Leave(bitmask []byte) {
|
||||
fs.tracer.Leave(bitmask)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +1,122 @@
|
||||
module source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.4
|
||||
|
||||
replace github.com/multiformats/go-multiaddr => ../go-multiaddr
|
||||
|
||||
replace github.com/multiformats/go-multiaddr-dns => ../go-multiaddr-dns
|
||||
|
||||
replace github.com/libp2p/go-libp2p => ../go-libp2p
|
||||
|
||||
replace github.com/libp2p/go-libp2p-gostream => ../go-libp2p-gostream
|
||||
|
||||
require (
|
||||
github.com/benbjohnson/clock v1.3.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/benbjohnson/clock v1.3.5
|
||||
github.com/ipfs/go-log/v2 v2.5.1
|
||||
github.com/libp2p/go-buffer-pool v0.1.0
|
||||
github.com/libp2p/go-libp2p v0.25.0
|
||||
github.com/libp2p/go-libp2p v0.35.4
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0
|
||||
github.com/libp2p/go-msgio v0.3.0
|
||||
github.com/multiformats/go-multiaddr v0.8.0
|
||||
github.com/multiformats/go-multiaddr v0.12.4
|
||||
github.com/multiformats/go-varint v0.0.7
|
||||
google.golang.org/protobuf v1.28.1
|
||||
google.golang.org/protobuf v1.34.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/cgroups v1.0.4 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/elastic/gosigar v0.14.2 // indirect
|
||||
github.com/flynn/noise v1.1.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect
|
||||
github.com/ipfs/go-cid v0.3.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/ipfs/go-cid v0.4.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.15 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/koron/go-ssdp v0.0.4 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
|
||||
github.com/libp2p/go-nat v0.2.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.1 // indirect
|
||||
github.com/libp2p/go-reuseport v0.2.0 // indirect
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
|
||||
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||
github.com/libp2p/go-yamux/v4 v4.0.1 // indirect
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.58 // indirect
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.1.1 // indirect
|
||||
github.com/multiformats/go-multicodec v0.7.0 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
||||
github.com/multiformats/go-multistream v0.4.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.5.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.9.0 // indirect
|
||||
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||
github.com/multiformats/go-multistream v0.5.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pion/datachannel v1.5.6 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.11 // indirect
|
||||
github.com/pion/ice/v2 v2.3.25 // indirect
|
||||
github.com/pion/interceptor v0.1.29 // indirect
|
||||
github.com/pion/logging v0.2.2 // indirect
|
||||
github.com/pion/mdns v0.0.12 // indirect
|
||||
github.com/pion/randutil v0.1.0 // indirect
|
||||
github.com/pion/rtcp v1.2.14 // indirect
|
||||
github.com/pion/rtp v1.8.6 // indirect
|
||||
github.com/pion/sctp v1.8.16 // indirect
|
||||
github.com/pion/sdp/v3 v3.0.9 // indirect
|
||||
github.com/pion/srtp/v2 v2.0.18 // indirect
|
||||
github.com/pion/stun v0.6.1 // indirect
|
||||
github.com/pion/transport/v2 v2.2.5 // indirect
|
||||
github.com/pion/turn/v2 v2.1.6 // indirect
|
||||
github.com/pion/webrtc/v3 v3.2.40 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.39.0 // indirect
|
||||
github.com/prometheus/procfs v0.9.0 // indirect
|
||||
github.com/quic-go/quic-go v0.37.5 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/quic-go v0.44.0 // indirect
|
||||
github.com/quic-go/webtransport-go v0.8.0 // indirect
|
||||
github.com/raulk/go-watchdog v1.3.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/testify v1.8.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
go.uber.org/dig v1.17.1 // indirect
|
||||
go.uber.org/fx v1.22.1 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/tools v0.21.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
)
|
||||
|
@ -10,8 +10,9 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
@ -21,24 +22,26 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
|
||||
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
|
||||
github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
|
||||
github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
@ -47,16 +50,18 @@ github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0
|
||||
github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4=
|
||||
github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
|
||||
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
@ -69,38 +74,45 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec h1:fR20TYVVwhK4O7r7y+McjRYyaTH6/vjwJOajE+XhlzM=
|
||||
github.com/google/pprof v0.0.0-20221203041831-ce31453925ec/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo=
|
||||
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
|
||||
github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
|
||||
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
||||
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
@ -109,13 +121,18 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
|
||||
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@ -127,32 +144,32 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6
|
||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
|
||||
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
|
||||
github.com/libp2p/go-libp2p v0.25.0 h1:ND6Hc6ZYCzC8S++C4mOD7LdPnLXRkNbr12/8FXgUfIo=
|
||||
github.com/libp2p/go-libp2p v0.25.0/go.mod h1:vXHmFpcfl+xIGN4qW58Bw3a0/SKGAesr5/T4IuJHE3o=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
|
||||
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
|
||||
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
||||
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
||||
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
|
||||
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
|
||||
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
|
||||
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
|
||||
github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560=
|
||||
github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
|
||||
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
|
||||
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
|
||||
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
|
||||
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
|
||||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
|
||||
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
|
||||
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
|
||||
@ -161,77 +178,122 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdn
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
|
||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
||||
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
|
||||
github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
|
||||
github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
|
||||
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
|
||||
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
|
||||
github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI=
|
||||
github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8=
|
||||
github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ=
|
||||
github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
|
||||
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
||||
github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108=
|
||||
github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc=
|
||||
github.com/multiformats/go-multistream v0.4.0 h1:5i4JbawClkbuaX+mIVXiHQYVPxUW+zjv6w7jtSRukxc=
|
||||
github.com/multiformats/go-multistream v0.4.0/go.mod h1:BS6ZSYcA4NwYEaIMeCtpJydp2Dc+fNRA6uJMSu/m8+4=
|
||||
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||
github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc=
|
||||
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
||||
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
|
||||
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
|
||||
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
||||
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
|
||||
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
|
||||
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
||||
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/onsi/ginkgo/v2 v2.5.1 h1:auzK7OI497k6x4OvWq+TKAcpcSAlod0doAH72oIN0Jw=
|
||||
github.com/onsi/ginkgo/v2 v2.5.1/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
|
||||
github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
|
||||
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
|
||||
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
||||
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
||||
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||
github.com/pion/ice/v2 v2.3.25 h1:M5rJA07dqhi3nobJIg+uPtcVjFECTrhcR3n0ns8kDZs=
|
||||
github.com/pion/ice/v2 v2.3.25/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
||||
github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
|
||||
github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
||||
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/rtp v1.8.6 h1:MTmn/b0aWWsAzux2AmP8WGllusBVw4NPYPVFFd7jUPw=
|
||||
github.com/pion/rtp v1.8.6/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=
|
||||
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
|
||||
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
|
||||
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
||||
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
||||
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
||||
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=
|
||||
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/transport/v2 v2.2.5 h1:iyi25i/21gQck4hfRhomF6SktmUQjRsRW4WJdhfc3Kc=
|
||||
github.com/pion/transport/v2 v2.2.5/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||
github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4=
|
||||
github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0=
|
||||
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
||||
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||
github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU=
|
||||
github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI=
|
||||
github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||
github.com/quic-go/webtransport-go v0.5.1 h1:1eVb7WDWCRoaeTtFHpFBJ6WDN1bSrPrRoW6tZgSw0Ow=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0=
|
||||
github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek=
|
||||
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
|
||||
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
|
||||
github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk=
|
||||
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
@ -266,15 +328,18 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
@ -282,18 +347,24 @@ github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMI
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
|
||||
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||
go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys=
|
||||
go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@ -303,11 +374,22 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
|
||||
golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp v0.0.0-20230725012225-302865e7556b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@ -317,8 +399,12 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -335,9 +421,19 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -351,7 +447,10 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -368,19 +467,52 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -392,11 +524,12 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -417,27 +550,28 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
|
||||
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
|
@ -45,7 +45,7 @@ func (gt *gossipTracer) Start(bs *BlossomSubRouter) {
|
||||
}
|
||||
|
||||
// track a promise to deliver a message from a list of msgIDs we are requesting
|
||||
func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
|
||||
func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs [][]byte) {
|
||||
if gt == nil {
|
||||
return
|
||||
}
|
||||
@ -54,12 +54,11 @@ func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
|
||||
mid := msgIDs[idx]
|
||||
|
||||
gt.Lock()
|
||||
defer gt.Unlock()
|
||||
|
||||
promises, ok := gt.promises[mid]
|
||||
promises, ok := gt.promises[string(mid)]
|
||||
if !ok {
|
||||
promises = make(map[peer.ID]time.Time)
|
||||
gt.promises[mid] = promises
|
||||
gt.promises[string(mid)] = promises
|
||||
}
|
||||
|
||||
_, ok = promises[p]
|
||||
@ -70,8 +69,10 @@ func (gt *gossipTracer) AddPromise(p peer.ID, msgIDs []string) {
|
||||
peerPromises = make(map[string]struct{})
|
||||
gt.peerPromises[p] = peerPromises
|
||||
}
|
||||
peerPromises[mid] = struct{}{}
|
||||
peerPromises[string(mid)] = struct{}{}
|
||||
}
|
||||
|
||||
gt.Unlock()
|
||||
}
|
||||
|
||||
// returns the number of broken promises for each peer who didn't follow up
|
||||
@ -82,7 +83,6 @@ func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int {
|
||||
}
|
||||
|
||||
gt.Lock()
|
||||
defer gt.Unlock()
|
||||
|
||||
var res map[peer.ID]int
|
||||
now := time.Now()
|
||||
@ -111,6 +111,7 @@ func (gt *gossipTracer) GetBrokenPromises() map[peer.ID]int {
|
||||
}
|
||||
}
|
||||
|
||||
gt.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
@ -120,24 +121,26 @@ func (gt *gossipTracer) fulfillPromise(msg *Message) {
|
||||
mid := gt.idGen.ID(msg)
|
||||
|
||||
gt.Lock()
|
||||
defer gt.Unlock()
|
||||
|
||||
promises, ok := gt.promises[mid]
|
||||
promises, ok := gt.promises[string(mid)]
|
||||
if !ok {
|
||||
gt.Unlock()
|
||||
return
|
||||
}
|
||||
delete(gt.promises, mid)
|
||||
delete(gt.promises, string(mid))
|
||||
|
||||
// delete the promise for all peers that promised it, as they have no way to fulfill it.
|
||||
for p := range promises {
|
||||
peerPromises, ok := gt.peerPromises[p]
|
||||
if ok {
|
||||
delete(peerPromises, mid)
|
||||
delete(peerPromises, string(mid))
|
||||
if len(peerPromises) == 0 {
|
||||
delete(gt.peerPromises, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gt.Unlock()
|
||||
}
|
||||
|
||||
func (gt *gossipTracer) DeliverMessage(msg *Message) {
|
||||
@ -181,10 +184,10 @@ func (gt *gossipTracer) UndeliverableMessage(msg *Message) {}
|
||||
|
||||
func (gt *gossipTracer) ThrottlePeer(p peer.ID) {
|
||||
gt.Lock()
|
||||
defer gt.Unlock()
|
||||
|
||||
peerPromises, ok := gt.peerPromises[p]
|
||||
if !ok {
|
||||
gt.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -197,4 +200,5 @@ func (gt *gossipTracer) ThrottlePeer(p peer.ID) {
|
||||
}
|
||||
|
||||
delete(gt.peerPromises, p)
|
||||
gt.Unlock()
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func TestBrokenPromises(t *testing.T) {
|
||||
peerB := peer.ID("B")
|
||||
peerC := peer.ID("C")
|
||||
|
||||
var mids []string
|
||||
var mids [][]byte
|
||||
for i := 0; i < 100; i++ {
|
||||
m := makeTestMessage(i)
|
||||
m.From = []byte(peerA)
|
||||
@ -72,7 +72,7 @@ func TestNoBrokenPromises(t *testing.T) {
|
||||
peerB := peer.ID("B")
|
||||
|
||||
var msgs []*pb.Message
|
||||
var mids []string
|
||||
var mids [][]byte
|
||||
for i := 0; i < 100; i++ {
|
||||
m := makeTestMessage(i)
|
||||
m.From = []byte(peerA)
|
||||
|
@ -30,7 +30,7 @@ func NewMessageCache(gossip, history int) *MessageCache {
|
||||
peertx: make(map[string]map[peer.ID]int),
|
||||
history: make([][]CacheEntry, history),
|
||||
gossip: gossip,
|
||||
msgID: func(msg *Message) string {
|
||||
msgID: func(msg *Message) []byte {
|
||||
return DefaultMsgIdFn(msg.Message)
|
||||
},
|
||||
}
|
||||
@ -41,47 +41,47 @@ type MessageCache struct {
|
||||
peertx map[string]map[peer.ID]int
|
||||
history [][]CacheEntry
|
||||
gossip int
|
||||
msgID func(*Message) string
|
||||
msgID func(*Message) []byte
|
||||
}
|
||||
|
||||
func (mc *MessageCache) SetMsgIdFn(msgID func(*Message) string) {
|
||||
func (mc *MessageCache) SetMsgIdFn(msgID func(*Message) []byte) {
|
||||
mc.msgID = msgID
|
||||
}
|
||||
|
||||
type CacheEntry struct {
|
||||
mid string
|
||||
mid []byte
|
||||
bitmask []byte
|
||||
}
|
||||
|
||||
func (mc *MessageCache) Put(msg *Message) {
|
||||
mid := mc.msgID(msg)
|
||||
mc.msgs[mid] = msg
|
||||
mc.msgs[string(mid)] = msg
|
||||
mc.history[0] = append(mc.history[0], CacheEntry{mid: mid, bitmask: msg.GetBitmask()})
|
||||
}
|
||||
|
||||
func (mc *MessageCache) Get(mid string) (*Message, bool) {
|
||||
m, ok := mc.msgs[mid]
|
||||
func (mc *MessageCache) Get(mid []byte) (*Message, bool) {
|
||||
m, ok := mc.msgs[string(mid)]
|
||||
return m, ok
|
||||
}
|
||||
|
||||
func (mc *MessageCache) GetForPeer(mid string, p peer.ID) (*Message, int, bool) {
|
||||
m, ok := mc.msgs[mid]
|
||||
func (mc *MessageCache) GetForPeer(mid []byte, p peer.ID) (*Message, int, bool) {
|
||||
m, ok := mc.msgs[string(mid)]
|
||||
if !ok {
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
tx, ok := mc.peertx[mid]
|
||||
tx, ok := mc.peertx[string(mid)]
|
||||
if !ok {
|
||||
tx = make(map[peer.ID]int)
|
||||
mc.peertx[mid] = tx
|
||||
mc.peertx[string(mid)] = tx
|
||||
}
|
||||
tx[p]++
|
||||
|
||||
return m, tx[p], true
|
||||
}
|
||||
|
||||
func (mc *MessageCache) GetGossipIDs(bitmask []byte) []string {
|
||||
var mids []string
|
||||
func (mc *MessageCache) GetGossipIDs(bitmask []byte) [][]byte {
|
||||
var mids [][]byte
|
||||
for _, entries := range mc.history[:mc.gossip] {
|
||||
for _, entry := range entries {
|
||||
if bytes.Equal(entry.bitmask, bitmask) {
|
||||
@ -95,8 +95,8 @@ func (mc *MessageCache) GetGossipIDs(bitmask []byte) []string {
|
||||
func (mc *MessageCache) Shift() {
|
||||
last := mc.history[len(mc.history)-1]
|
||||
for _, entry := range last {
|
||||
delete(mc.msgs, entry.mid)
|
||||
delete(mc.peertx, entry.mid)
|
||||
delete(mc.msgs, string(entry.mid))
|
||||
delete(mc.peertx, string(entry.mid))
|
||||
}
|
||||
for i := len(mc.history) - 2; i >= 0; i-- {
|
||||
mc.history[i+1] = mc.history[i]
|
||||
|
@ -1,6 +1,7 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
@ -33,14 +34,14 @@ func TestMessageCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
gids := mcache.GetGossipIDs([]byte{0x7e, 0x57})
|
||||
gids := mcache.GetGossipIDs([]byte{0x01, 0x00})
|
||||
if len(gids) != 10 {
|
||||
t.Fatalf("Expected 10 gossip IDs; got %d", len(gids))
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
mid := msgID(msgs[i])
|
||||
if mid != gids[i] {
|
||||
if !bytes.Equal(mid, gids[i]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
@ -62,21 +63,21 @@ func TestMessageCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
gids = mcache.GetGossipIDs([]byte{0x7e, 0x57})
|
||||
gids = mcache.GetGossipIDs([]byte{0x01, 0x00})
|
||||
if len(gids) != 20 {
|
||||
t.Fatalf("Expected 20 gossip IDs; got %d", len(gids))
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
mid := msgID(msgs[i])
|
||||
if mid != gids[10+i] {
|
||||
if !bytes.Equal(mid, gids[10+i]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 10; i < 20; i++ {
|
||||
mid := msgID(msgs[i])
|
||||
if mid != gids[i-10] {
|
||||
if !bytes.Equal(mid, gids[i-10]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
@ -125,28 +126,28 @@ func TestMessageCache(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
gids = mcache.GetGossipIDs([]byte{0x7e, 0x57})
|
||||
gids = mcache.GetGossipIDs([]byte{0x01, 0x00})
|
||||
if len(gids) != 30 {
|
||||
t.Fatalf("Expected 30 gossip IDs; got %d", len(gids))
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
mid := msgID(msgs[50+i])
|
||||
if mid != gids[i] {
|
||||
if !bytes.Equal(mid, gids[i]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 10; i < 20; i++ {
|
||||
mid := msgID(msgs[30+i])
|
||||
if mid != gids[i] {
|
||||
if !bytes.Equal(mid, gids[i]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
for i := 20; i < 30; i++ {
|
||||
mid := msgID(msgs[10+i])
|
||||
if mid != gids[i] {
|
||||
if !bytes.Equal(mid, gids[i]) {
|
||||
t.Fatalf("GossipID mismatch for message %d", i)
|
||||
}
|
||||
}
|
||||
@ -157,11 +158,11 @@ func makeTestMessage(n int) *pb.Message {
|
||||
seqno := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(seqno, uint64(n))
|
||||
data := []byte(fmt.Sprintf("%d", n))
|
||||
bitmask := []byte{0x7e, 0x57}
|
||||
bitmask := []byte{0x01, 0x00}
|
||||
return &pb.Message{
|
||||
Data: data,
|
||||
Bitmask: bitmask,
|
||||
From: []byte([]byte{0x7e, 0x57}),
|
||||
From: []byte([]byte{0x01, 0x00}),
|
||||
Seqno: seqno,
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ func (m *msgIDGenerator) Set(bitmask []byte, gen MsgIdFunction) {
|
||||
}
|
||||
|
||||
// ID computes ID for the msg or short-circuits with the cached value.
|
||||
func (m *msgIDGenerator) ID(msg *Message) string {
|
||||
if msg.ID != "" {
|
||||
func (m *msgIDGenerator) ID(msg *Message) []byte {
|
||||
if len(msg.ID) != 0 {
|
||||
return msg.ID
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ func (m *msgIDGenerator) ID(msg *Message) string {
|
||||
}
|
||||
|
||||
// RawID computes ID for the proto 'msg'.
|
||||
func (m *msgIDGenerator) RawID(msg *pb.Message) string {
|
||||
func (m *msgIDGenerator) RawID(msg *pb.Message) []byte {
|
||||
m.bitmaskGensLk.RLock()
|
||||
gen, ok := m.bitmaskGens[string(msg.GetBitmask())]
|
||||
m.bitmaskGensLk.RUnlock()
|
||||
|
@ -1,75 +0,0 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
var _ network.Notifiee = (*PubSubNotif)(nil)
|
||||
|
||||
type PubSubNotif PubSub
|
||||
|
||||
func (p *PubSubNotif) OpenedStream(n network.Network, s network.Stream) {
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) ClosedStream(n network.Network, s network.Stream) {
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) Connected(n network.Network, c network.Conn) {
|
||||
// ignore transient connections
|
||||
if c.Stat().Limited {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
p.newPeersPrioLk.RLock()
|
||||
p.newPeersMx.Lock()
|
||||
p.newPeersPend[c.RemotePeer()] = struct{}{}
|
||||
p.newPeersMx.Unlock()
|
||||
p.newPeersPrioLk.RUnlock()
|
||||
|
||||
select {
|
||||
case p.newPeers <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) Disconnected(n network.Network, c network.Conn) {
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) Listen(n network.Network, _ ma.Multiaddr) {
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) ListenClose(n network.Network, _ ma.Multiaddr) {
|
||||
}
|
||||
|
||||
func (p *PubSubNotif) Initialize() {
|
||||
isTransient := func(pid peer.ID) bool {
|
||||
for _, c := range p.host.Network().ConnsToPeer(pid) {
|
||||
if !c.Stat().Limited {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
p.newPeersPrioLk.RLock()
|
||||
p.newPeersMx.Lock()
|
||||
for _, pid := range p.host.Network().Peers() {
|
||||
if isTransient(pid) {
|
||||
continue
|
||||
}
|
||||
|
||||
p.newPeersPend[pid] = struct{}{}
|
||||
}
|
||||
p.newPeersMx.Unlock()
|
||||
p.newPeersPrioLk.RUnlock()
|
||||
|
||||
select {
|
||||
case p.newPeers <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
@ -246,9 +246,8 @@ type ControlIHave struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Bitmask []byte `protobuf:"bytes,1,opt,name=bitmask,proto3" json:"bitmask,omitempty"`
|
||||
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
|
||||
MessageIDs []string `protobuf:"bytes,2,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
|
||||
Bitmask []byte `protobuf:"bytes,1,opt,name=bitmask,proto3" json:"bitmask,omitempty"`
|
||||
MessageIDs [][]byte `protobuf:"bytes,2,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ControlIHave) Reset() {
|
||||
@ -290,7 +289,7 @@ func (x *ControlIHave) GetBitmask() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ControlIHave) GetMessageIDs() []string {
|
||||
func (x *ControlIHave) GetMessageIDs() [][]byte {
|
||||
if x != nil {
|
||||
return x.MessageIDs
|
||||
}
|
||||
@ -302,8 +301,7 @@ type ControlIWant struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
|
||||
MessageIDs []string `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
|
||||
MessageIDs [][]byte `protobuf:"bytes,1,rep,name=messageIDs,proto3" json:"messageIDs,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ControlIWant) Reset() {
|
||||
@ -338,7 +336,7 @@ func (*ControlIWant) Descriptor() ([]byte, []int) {
|
||||
return file_rpc_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ControlIWant) GetMessageIDs() []string {
|
||||
func (x *ControlIWant) GetMessageIDs() [][]byte {
|
||||
if x != nil {
|
||||
return x.MessageIDs
|
||||
}
|
||||
@ -612,10 +610,10 @@ var file_rpc_proto_rawDesc = []byte{
|
||||
0x49, 0x48, 0x61, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
|
||||
0x2e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x12,
|
||||
0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x22,
|
||||
0x28, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0x71, 0x0a, 0x0c, 0x43, 0x6f, 0x6e,
|
||||
|
@ -34,13 +34,11 @@ message ControlMessage {
|
||||
|
||||
message ControlIHave {
|
||||
bytes bitmask = 1;
|
||||
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
|
||||
repeated string messageIDs = 2;
|
||||
repeated bytes messageIDs = 2;
|
||||
}
|
||||
|
||||
message ControlIWant {
|
||||
// implementors from other languages should use bytes here - go protobuf emits invalid utf8 strings
|
||||
repeated string messageIDs = 1;
|
||||
repeated bytes messageIDs = 1;
|
||||
}
|
||||
|
||||
message ControlGraft {
|
||||
|
@ -23,19 +23,20 @@ const (
|
||||
type TraceEvent_Type int32
|
||||
|
||||
const (
|
||||
TraceEvent_PUBLISH_MESSAGE TraceEvent_Type = 0
|
||||
TraceEvent_REJECT_MESSAGE TraceEvent_Type = 1
|
||||
TraceEvent_DUPLICATE_MESSAGE TraceEvent_Type = 2
|
||||
TraceEvent_DELIVER_MESSAGE TraceEvent_Type = 3
|
||||
TraceEvent_ADD_PEER TraceEvent_Type = 4
|
||||
TraceEvent_REMOVE_PEER TraceEvent_Type = 5
|
||||
TraceEvent_RECV_RPC TraceEvent_Type = 6
|
||||
TraceEvent_SEND_RPC TraceEvent_Type = 7
|
||||
TraceEvent_DROP_RPC TraceEvent_Type = 8
|
||||
TraceEvent_JOIN TraceEvent_Type = 9
|
||||
TraceEvent_LEAVE TraceEvent_Type = 10
|
||||
TraceEvent_GRAFT TraceEvent_Type = 11
|
||||
TraceEvent_PRUNE TraceEvent_Type = 12
|
||||
TraceEvent_PUBLISH_MESSAGE TraceEvent_Type = 0
|
||||
TraceEvent_REJECT_MESSAGE TraceEvent_Type = 1
|
||||
TraceEvent_DUPLICATE_MESSAGE TraceEvent_Type = 2
|
||||
TraceEvent_DELIVER_MESSAGE TraceEvent_Type = 3
|
||||
TraceEvent_ADD_PEER TraceEvent_Type = 4
|
||||
TraceEvent_REMOVE_PEER TraceEvent_Type = 5
|
||||
TraceEvent_RECV_RPC TraceEvent_Type = 6
|
||||
TraceEvent_SEND_RPC TraceEvent_Type = 7
|
||||
TraceEvent_DROP_RPC TraceEvent_Type = 8
|
||||
TraceEvent_JOIN TraceEvent_Type = 9
|
||||
TraceEvent_LEAVE TraceEvent_Type = 10
|
||||
TraceEvent_GRAFT TraceEvent_Type = 11
|
||||
TraceEvent_PRUNE TraceEvent_Type = 12
|
||||
TraceEvent_UNDELIVERABLE_MESSAGE TraceEvent_Type = 13
|
||||
)
|
||||
|
||||
// Enum value maps for TraceEvent_Type.
|
||||
@ -54,21 +55,23 @@ var (
|
||||
10: "LEAVE",
|
||||
11: "GRAFT",
|
||||
12: "PRUNE",
|
||||
13: "UNDELIVERABLE_MESSAGE",
|
||||
}
|
||||
TraceEvent_Type_value = map[string]int32{
|
||||
"PUBLISH_MESSAGE": 0,
|
||||
"REJECT_MESSAGE": 1,
|
||||
"DUPLICATE_MESSAGE": 2,
|
||||
"DELIVER_MESSAGE": 3,
|
||||
"ADD_PEER": 4,
|
||||
"REMOVE_PEER": 5,
|
||||
"RECV_RPC": 6,
|
||||
"SEND_RPC": 7,
|
||||
"DROP_RPC": 8,
|
||||
"JOIN": 9,
|
||||
"LEAVE": 10,
|
||||
"GRAFT": 11,
|
||||
"PRUNE": 12,
|
||||
"PUBLISH_MESSAGE": 0,
|
||||
"REJECT_MESSAGE": 1,
|
||||
"DUPLICATE_MESSAGE": 2,
|
||||
"DELIVER_MESSAGE": 3,
|
||||
"ADD_PEER": 4,
|
||||
"REMOVE_PEER": 5,
|
||||
"RECV_RPC": 6,
|
||||
"SEND_RPC": 7,
|
||||
"DROP_RPC": 8,
|
||||
"JOIN": 9,
|
||||
"LEAVE": 10,
|
||||
"GRAFT": 11,
|
||||
"PRUNE": 12,
|
||||
"UNDELIVERABLE_MESSAGE": 13,
|
||||
}
|
||||
)
|
||||
|
||||
@ -104,22 +107,23 @@ type TraceEvent struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Type *TraceEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=blossomsub.pb.TraceEvent_Type,oneof" json:"type,omitempty"`
|
||||
PeerID []byte `protobuf:"bytes,2,opt,name=peerID,proto3,oneof" json:"peerID,omitempty"`
|
||||
Timestamp *int64 `protobuf:"varint,3,opt,name=timestamp,proto3,oneof" json:"timestamp,omitempty"`
|
||||
PublishMessage *TraceEvent_PublishMessage `protobuf:"bytes,4,opt,name=publishMessage,proto3,oneof" json:"publishMessage,omitempty"`
|
||||
RejectMessage *TraceEvent_RejectMessage `protobuf:"bytes,5,opt,name=rejectMessage,proto3,oneof" json:"rejectMessage,omitempty"`
|
||||
DuplicateMessage *TraceEvent_DuplicateMessage `protobuf:"bytes,6,opt,name=duplicateMessage,proto3,oneof" json:"duplicateMessage,omitempty"`
|
||||
DeliverMessage *TraceEvent_DeliverMessage `protobuf:"bytes,7,opt,name=deliverMessage,proto3,oneof" json:"deliverMessage,omitempty"`
|
||||
AddPeer *TraceEvent_AddPeer `protobuf:"bytes,8,opt,name=addPeer,proto3,oneof" json:"addPeer,omitempty"`
|
||||
RemovePeer *TraceEvent_RemovePeer `protobuf:"bytes,9,opt,name=removePeer,proto3,oneof" json:"removePeer,omitempty"`
|
||||
RecvRPC *TraceEvent_RecvRPC `protobuf:"bytes,10,opt,name=recvRPC,proto3,oneof" json:"recvRPC,omitempty"`
|
||||
SendRPC *TraceEvent_SendRPC `protobuf:"bytes,11,opt,name=sendRPC,proto3,oneof" json:"sendRPC,omitempty"`
|
||||
DropRPC *TraceEvent_DropRPC `protobuf:"bytes,12,opt,name=dropRPC,proto3,oneof" json:"dropRPC,omitempty"`
|
||||
Join *TraceEvent_Join `protobuf:"bytes,13,opt,name=join,proto3,oneof" json:"join,omitempty"`
|
||||
Leave *TraceEvent_Leave `protobuf:"bytes,14,opt,name=leave,proto3,oneof" json:"leave,omitempty"`
|
||||
Graft *TraceEvent_Graft `protobuf:"bytes,15,opt,name=graft,proto3,oneof" json:"graft,omitempty"`
|
||||
Prune *TraceEvent_Prune `protobuf:"bytes,16,opt,name=prune,proto3,oneof" json:"prune,omitempty"`
|
||||
Type *TraceEvent_Type `protobuf:"varint,1,opt,name=type,proto3,enum=blossomsub.pb.TraceEvent_Type,oneof" json:"type,omitempty"`
|
||||
PeerID []byte `protobuf:"bytes,2,opt,name=peerID,proto3,oneof" json:"peerID,omitempty"`
|
||||
Timestamp *int64 `protobuf:"varint,3,opt,name=timestamp,proto3,oneof" json:"timestamp,omitempty"`
|
||||
PublishMessage *TraceEvent_PublishMessage `protobuf:"bytes,4,opt,name=publishMessage,proto3,oneof" json:"publishMessage,omitempty"`
|
||||
RejectMessage *TraceEvent_RejectMessage `protobuf:"bytes,5,opt,name=rejectMessage,proto3,oneof" json:"rejectMessage,omitempty"`
|
||||
DuplicateMessage *TraceEvent_DuplicateMessage `protobuf:"bytes,6,opt,name=duplicateMessage,proto3,oneof" json:"duplicateMessage,omitempty"`
|
||||
DeliverMessage *TraceEvent_DeliverMessage `protobuf:"bytes,7,opt,name=deliverMessage,proto3,oneof" json:"deliverMessage,omitempty"`
|
||||
AddPeer *TraceEvent_AddPeer `protobuf:"bytes,8,opt,name=addPeer,proto3,oneof" json:"addPeer,omitempty"`
|
||||
RemovePeer *TraceEvent_RemovePeer `protobuf:"bytes,9,opt,name=removePeer,proto3,oneof" json:"removePeer,omitempty"`
|
||||
RecvRPC *TraceEvent_RecvRPC `protobuf:"bytes,10,opt,name=recvRPC,proto3,oneof" json:"recvRPC,omitempty"`
|
||||
SendRPC *TraceEvent_SendRPC `protobuf:"bytes,11,opt,name=sendRPC,proto3,oneof" json:"sendRPC,omitempty"`
|
||||
DropRPC *TraceEvent_DropRPC `protobuf:"bytes,12,opt,name=dropRPC,proto3,oneof" json:"dropRPC,omitempty"`
|
||||
Join *TraceEvent_Join `protobuf:"bytes,13,opt,name=join,proto3,oneof" json:"join,omitempty"`
|
||||
Leave *TraceEvent_Leave `protobuf:"bytes,14,opt,name=leave,proto3,oneof" json:"leave,omitempty"`
|
||||
Graft *TraceEvent_Graft `protobuf:"bytes,15,opt,name=graft,proto3,oneof" json:"graft,omitempty"`
|
||||
Prune *TraceEvent_Prune `protobuf:"bytes,16,opt,name=prune,proto3,oneof" json:"prune,omitempty"`
|
||||
UndeliverableMessage *TraceEvent_UndeliverableMessage `protobuf:"bytes,17,opt,name=undeliverableMessage,proto3,oneof" json:"undeliverableMessage,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TraceEvent) Reset() {
|
||||
@ -266,6 +270,13 @@ func (x *TraceEvent) GetPrune() *TraceEvent_Prune {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TraceEvent) GetUndeliverableMessage() *TraceEvent_UndeliverableMessage {
|
||||
if x != nil {
|
||||
return x.UndeliverableMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TraceEventBatch struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1036,6 +1047,69 @@ func (x *TraceEvent_Prune) GetBitmask() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TraceEvent_UndeliverableMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
MessageID []byte `protobuf:"bytes,1,opt,name=messageID,proto3,oneof" json:"messageID,omitempty"`
|
||||
Bitmask []byte `protobuf:"bytes,2,opt,name=bitmask,proto3,oneof" json:"bitmask,omitempty"`
|
||||
ReceivedFrom []byte `protobuf:"bytes,3,opt,name=receivedFrom,proto3,oneof" json:"receivedFrom,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) Reset() {
|
||||
*x = TraceEvent_UndeliverableMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TraceEvent_UndeliverableMessage) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[15]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TraceEvent_UndeliverableMessage.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_UndeliverableMessage) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 13}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) GetMessageID() []byte {
|
||||
if x != nil {
|
||||
return x.MessageID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) GetBitmask() []byte {
|
||||
if x != nil {
|
||||
return x.Bitmask
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TraceEvent_UndeliverableMessage) GetReceivedFrom() []byte {
|
||||
if x != nil {
|
||||
return x.ReceivedFrom
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TraceEvent_RPCMeta struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1049,7 +1123,7 @@ type TraceEvent_RPCMeta struct {
|
||||
func (x *TraceEvent_RPCMeta) Reset() {
|
||||
*x = TraceEvent_RPCMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[15]
|
||||
mi := &file_trace_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1062,7 +1136,7 @@ func (x *TraceEvent_RPCMeta) String() string {
|
||||
func (*TraceEvent_RPCMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_RPCMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[15]
|
||||
mi := &file_trace_proto_msgTypes[16]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1075,7 +1149,7 @@ func (x *TraceEvent_RPCMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_RPCMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_RPCMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 13}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 14}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_RPCMeta) GetMessages() []*TraceEvent_MessageMeta {
|
||||
@ -1111,7 +1185,7 @@ type TraceEvent_MessageMeta struct {
|
||||
func (x *TraceEvent_MessageMeta) Reset() {
|
||||
*x = TraceEvent_MessageMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[16]
|
||||
mi := &file_trace_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1124,7 +1198,7 @@ func (x *TraceEvent_MessageMeta) String() string {
|
||||
func (*TraceEvent_MessageMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_MessageMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[16]
|
||||
mi := &file_trace_proto_msgTypes[17]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1137,7 +1211,7 @@ func (x *TraceEvent_MessageMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_MessageMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_MessageMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 14}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 15}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_MessageMeta) GetMessageID() []byte {
|
||||
@ -1166,7 +1240,7 @@ type TraceEvent_SubMeta struct {
|
||||
func (x *TraceEvent_SubMeta) Reset() {
|
||||
*x = TraceEvent_SubMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[17]
|
||||
mi := &file_trace_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1179,7 +1253,7 @@ func (x *TraceEvent_SubMeta) String() string {
|
||||
func (*TraceEvent_SubMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_SubMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[17]
|
||||
mi := &file_trace_proto_msgTypes[18]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1192,7 +1266,7 @@ func (x *TraceEvent_SubMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_SubMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_SubMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 15}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 16}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_SubMeta) GetSubscribe() bool {
|
||||
@ -1223,7 +1297,7 @@ type TraceEvent_ControlMeta struct {
|
||||
func (x *TraceEvent_ControlMeta) Reset() {
|
||||
*x = TraceEvent_ControlMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[18]
|
||||
mi := &file_trace_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1236,7 +1310,7 @@ func (x *TraceEvent_ControlMeta) String() string {
|
||||
func (*TraceEvent_ControlMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_ControlMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[18]
|
||||
mi := &file_trace_proto_msgTypes[19]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1249,7 +1323,7 @@ func (x *TraceEvent_ControlMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_ControlMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_ControlMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 16}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 17}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_ControlMeta) GetIhave() []*TraceEvent_ControlIHaveMeta {
|
||||
@ -1292,7 +1366,7 @@ type TraceEvent_ControlIHaveMeta struct {
|
||||
func (x *TraceEvent_ControlIHaveMeta) Reset() {
|
||||
*x = TraceEvent_ControlIHaveMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[19]
|
||||
mi := &file_trace_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1305,7 +1379,7 @@ func (x *TraceEvent_ControlIHaveMeta) String() string {
|
||||
func (*TraceEvent_ControlIHaveMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_ControlIHaveMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[19]
|
||||
mi := &file_trace_proto_msgTypes[20]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1318,7 +1392,7 @@ func (x *TraceEvent_ControlIHaveMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_ControlIHaveMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_ControlIHaveMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 17}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 18}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_ControlIHaveMeta) GetBitmask() []byte {
|
||||
@ -1346,7 +1420,7 @@ type TraceEvent_ControlIWantMeta struct {
|
||||
func (x *TraceEvent_ControlIWantMeta) Reset() {
|
||||
*x = TraceEvent_ControlIWantMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[20]
|
||||
mi := &file_trace_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1359,7 +1433,7 @@ func (x *TraceEvent_ControlIWantMeta) String() string {
|
||||
func (*TraceEvent_ControlIWantMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_ControlIWantMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[20]
|
||||
mi := &file_trace_proto_msgTypes[21]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1372,7 +1446,7 @@ func (x *TraceEvent_ControlIWantMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_ControlIWantMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_ControlIWantMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 18}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 19}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_ControlIWantMeta) GetMessageIDs() [][]byte {
|
||||
@ -1393,7 +1467,7 @@ type TraceEvent_ControlGraftMeta struct {
|
||||
func (x *TraceEvent_ControlGraftMeta) Reset() {
|
||||
*x = TraceEvent_ControlGraftMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[21]
|
||||
mi := &file_trace_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1406,7 +1480,7 @@ func (x *TraceEvent_ControlGraftMeta) String() string {
|
||||
func (*TraceEvent_ControlGraftMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_ControlGraftMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[21]
|
||||
mi := &file_trace_proto_msgTypes[22]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1419,7 +1493,7 @@ func (x *TraceEvent_ControlGraftMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_ControlGraftMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_ControlGraftMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 19}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 20}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_ControlGraftMeta) GetBitmask() []byte {
|
||||
@ -1441,7 +1515,7 @@ type TraceEvent_ControlPruneMeta struct {
|
||||
func (x *TraceEvent_ControlPruneMeta) Reset() {
|
||||
*x = TraceEvent_ControlPruneMeta{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_trace_proto_msgTypes[22]
|
||||
mi := &file_trace_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1454,7 +1528,7 @@ func (x *TraceEvent_ControlPruneMeta) String() string {
|
||||
func (*TraceEvent_ControlPruneMeta) ProtoMessage() {}
|
||||
|
||||
func (x *TraceEvent_ControlPruneMeta) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_trace_proto_msgTypes[22]
|
||||
mi := &file_trace_proto_msgTypes[23]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1467,7 +1541,7 @@ func (x *TraceEvent_ControlPruneMeta) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TraceEvent_ControlPruneMeta.ProtoReflect.Descriptor instead.
|
||||
func (*TraceEvent_ControlPruneMeta) Descriptor() ([]byte, []int) {
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 20}
|
||||
return file_trace_proto_rawDescGZIP(), []int{0, 21}
|
||||
}
|
||||
|
||||
func (x *TraceEvent_ControlPruneMeta) GetBitmask() []byte {
|
||||
@ -1488,7 +1562,7 @@ var File_trace_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_trace_proto_rawDesc = []byte{
|
||||
0x0a, 0x0b, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x62,
|
||||
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xfe, 0x1e, 0x0a,
|
||||
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x22, 0xca, 0x21, 0x0a,
|
||||
0x0a, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x37, 0x0a, 0x04, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x62, 0x6c, 0x6f, 0x73,
|
||||
0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45,
|
||||
@ -1555,38 +1629,106 @@ var file_trace_proto_rawDesc = []byte{
|
||||
0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
|
||||
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x48, 0x0f, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65,
|
||||
0x88, 0x01, 0x01, 0x1a, 0x6c, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74,
|
||||
0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x1a, 0xcd, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
|
||||
0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c,
|
||||
0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12,
|
||||
0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48,
|
||||
0x02, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52,
|
||||
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65,
|
||||
0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72,
|
||||
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x1a, 0xa8, 0x01, 0x0a, 0x10, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63,
|
||||
0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88,
|
||||
0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01,
|
||||
0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42,
|
||||
0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d,
|
||||
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa6, 0x01, 0x0a,
|
||||
0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x88, 0x01, 0x01, 0x12, 0x67, 0x0a, 0x14, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2e, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70,
|
||||
0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x55, 0x6e, 0x64,
|
||||
0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x48, 0x10, 0x52, 0x14, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x1a, 0x6c, 0x0a, 0x0e,
|
||||
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21,
|
||||
0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01,
|
||||
0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01,
|
||||
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a,
|
||||
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xcd, 0x01, 0x0a, 0x0d, 0x52,
|
||||
0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12,
|
||||
0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
|
||||
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73,
|
||||
0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46,
|
||||
0x72, 0x6f, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x42, 0x0a,
|
||||
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa8, 0x01, 0x0a, 0x10, 0x44,
|
||||
0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88,
|
||||
0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72,
|
||||
0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65,
|
||||
0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, 0x07,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63,
|
||||
0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69,
|
||||
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xa6, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65,
|
||||
0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65,
|
||||
0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x48, 0x02, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d,
|
||||
0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
|
||||
0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x42, 0x0f, 0x0a,
|
||||
0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0x56,
|
||||
0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x88, 0x01,
|
||||
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x08, 0x0a, 0x06,
|
||||
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x0a, 0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
|
||||
0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01,
|
||||
0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x1a, 0x88, 0x01, 0x0a,
|
||||
0x07, 0x52, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x12, 0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65,
|
||||
0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
|
||||
0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01,
|
||||
0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a,
|
||||
0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x07,
|
||||
0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x52,
|
||||
0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12,
|
||||
0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e,
|
||||
0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72,
|
||||
0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f,
|
||||
0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a,
|
||||
0x76, 0x0a, 0x07, 0x44, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65,
|
||||
0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65,
|
||||
0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73,
|
||||
0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74,
|
||||
0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61,
|
||||
0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07,
|
||||
0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x31, 0x0a, 0x04, 0x4a, 0x6f, 0x69, 0x6e, 0x12,
|
||||
0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a,
|
||||
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x05, 0x4c, 0x65,
|
||||
0x61, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88,
|
||||
0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a,
|
||||
0x0a, 0x05, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49,
|
||||
0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a,
|
||||
0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x50, 0x72,
|
||||
0x75, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01,
|
||||
0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42,
|
||||
0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xac, 0x01, 0x0a, 0x14, 0x55, 0x6e, 0x64, 0x65, 0x6c,
|
||||
0x69, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88,
|
||||
0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20,
|
||||
@ -1596,156 +1738,109 @@ var file_trace_proto_rawDesc = []byte{
|
||||
0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74,
|
||||
0x6d, 0x61, 0x73, 0x6b, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0x56, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72,
|
||||
0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x48, 0x00, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a,
|
||||
0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x44, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x34, 0x0a,
|
||||
0x0a, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x06, 0x70,
|
||||
0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70,
|
||||
0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65,
|
||||
0x72, 0x49, 0x44, 0x1a, 0x88, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x12,
|
||||
0x27, 0x0a, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
|
||||
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65,
|
||||
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76,
|
||||
0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e,
|
||||
0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e,
|
||||
0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
|
||||
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88,
|
||||
0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a,
|
||||
0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x76, 0x0a, 0x07, 0x44, 0x72, 0x6f, 0x70, 0x52, 0x50,
|
||||
0x43, 0x12, 0x1b, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x3a,
|
||||
0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62,
|
||||
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61,
|
||||
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x48,
|
||||
0x01, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73,
|
||||
0x65, 0x6e, 0x64, 0x54, 0x6f, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x31,
|
||||
0x0a, 0x04, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x1a, 0x32, 0x0a, 0x05, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69,
|
||||
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69,
|
||||
0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x47, 0x72, 0x61, 0x66, 0x74, 0x12, 0x1b,
|
||||
0x0a, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
|
||||
0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70,
|
||||
0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x1a, 0x5a, 0x0a, 0x05, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x65,
|
||||
0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x65,
|
||||
0x65, 0x72, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49,
|
||||
0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0xe5, 0x01,
|
||||
0x0a, 0x07, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x08, 0x6d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c,
|
||||
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63,
|
||||
0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0c,
|
||||
0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75,
|
||||
0x62, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
|
||||
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x1a, 0x69, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d,
|
||||
0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x1a, 0x65, 0x0a, 0x07, 0x53, 0x75, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x73,
|
||||
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00,
|
||||
0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d,
|
||||
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a,
|
||||
0x0a, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x95, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
|
||||
0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e,
|
||||
0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x52, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x77, 0x61,
|
||||
0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73,
|
||||
0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76,
|
||||
0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74,
|
||||
0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x77, 0x61, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x67,
|
||||
0x72, 0x61, 0x66, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f,
|
||||
0x64, 0x46, 0x72, 0x6f, 0x6d, 0x1a, 0xe5, 0x01, 0x0a, 0x07, 0x52, 0x50, 0x43, 0x4d, 0x65, 0x74,
|
||||
0x61, 0x12, 0x41, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62,
|
||||
0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4d,
|
||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x73, 0x12, 0x45, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x62, 0x6c, 0x6f,
|
||||
0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65,
|
||||
0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61,
|
||||
0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x67, 0x72, 0x61, 0x66, 0x74, 0x12, 0x40, 0x0a,
|
||||
0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62,
|
||||
0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0c, 0x73,
|
||||
0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x07, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x62,
|
||||
0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61,
|
||||
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50,
|
||||
0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x1a,
|
||||
0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88,
|
||||
0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
|
||||
0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32,
|
||||
0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65,
|
||||
0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49,
|
||||
0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61,
|
||||
0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e,
|
||||
0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0xcf, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x13, 0x0a, 0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
|
||||
0x47, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d,
|
||||
0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c,
|
||||
0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12,
|
||||
0x13, 0x0a, 0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
|
||||
0x47, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52,
|
||||
0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45,
|
||||
0x52, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10,
|
||||
0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12,
|
||||
0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a,
|
||||
0x04, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45,
|
||||
0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52, 0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a,
|
||||
0x05, 0x50, 0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x79, 0x70,
|
||||
0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42, 0x0c, 0x0a, 0x0a,
|
||||
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x70,
|
||||
0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x10, 0x0a,
|
||||
0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42,
|
||||
0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
|
||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61, 0x64, 0x64, 0x50,
|
||||
0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65,
|
||||
0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x52, 0x50, 0x43, 0x42, 0x0a,
|
||||
0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64,
|
||||
0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x42,
|
||||
0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x67, 0x72,
|
||||
0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x22, 0x42, 0x0a,
|
||||
0x0f, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68,
|
||||
0x12, 0x2f, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x19, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e,
|
||||
0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63,
|
||||
0x68, 0x42, 0x43, 0x5a, 0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c,
|
||||
0x69, 0x62, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69,
|
||||
0x62, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67,
|
||||
0x6f, 0x2d, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d,
|
||||
0x73, 0x75, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x88, 0x01,
|
||||
0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x1a, 0x69, 0x0a,
|
||||
0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x00, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12,
|
||||
0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c,
|
||||
0x0a, 0x0a, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0a, 0x0a, 0x08,
|
||||
0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x65, 0x0a, 0x07, 0x53, 0x75, 0x62, 0x4d,
|
||||
0x65, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
|
||||
0x69, 0x62, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73,
|
||||
0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61,
|
||||
0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
|
||||
0x69, 0x62, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a,
|
||||
0x95, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x4d, 0x65, 0x74, 0x61, 0x12,
|
||||
0x40, 0x0a, 0x05, 0x69, 0x68, 0x61, 0x76, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a,
|
||||
0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54,
|
||||
0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x68, 0x61, 0x76,
|
||||
0x65, 0x12, 0x40, 0x0a, 0x05, 0x69, 0x77, 0x61, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62,
|
||||
0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x72, 0x6f, 0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05, 0x69, 0x77,
|
||||
0x61, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x67, 0x72, 0x61, 0x66, 0x74, 0x18, 0x03, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e,
|
||||
0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x05,
|
||||
0x67, 0x72, 0x61, 0x66, 0x74, 0x12, 0x40, 0x0a, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x18, 0x04,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75,
|
||||
0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61,
|
||||
0x52, 0x05, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x1a, 0x5d, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72,
|
||||
0x6f, 0x6c, 0x49, 0x48, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x07, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07,
|
||||
0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62,
|
||||
0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x32, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||
0x6c, 0x49, 0x57, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65,
|
||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a,
|
||||
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x73, 0x1a, 0x3d, 0x0a, 0x10, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x47, 0x72, 0x61, 0x66, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d,
|
||||
0x0a, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48,
|
||||
0x00, 0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a,
|
||||
0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x1a, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x72, 0x6f, 0x6c, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1d, 0x0a,
|
||||
0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00,
|
||||
0x52, 0x07, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05,
|
||||
0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x70, 0x65, 0x65,
|
||||
0x72, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x73, 0x6b, 0x22, 0xea,
|
||||
0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x55, 0x42, 0x4c, 0x49,
|
||||
0x53, 0x48, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e,
|
||||
0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01,
|
||||
0x12, 0x15, 0x0a, 0x11, 0x44, 0x55, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45,
|
||||
0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x45, 0x4c, 0x49, 0x56,
|
||||
0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08,
|
||||
0x41, 0x44, 0x44, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x45,
|
||||
0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x50, 0x45, 0x45, 0x52, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52,
|
||||
0x45, 0x43, 0x56, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x06, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x45, 0x4e,
|
||||
0x44, 0x5f, 0x52, 0x50, 0x43, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x52, 0x4f, 0x50, 0x5f,
|
||||
0x52, 0x50, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x09, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x52,
|
||||
0x41, 0x46, 0x54, 0x10, 0x0b, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x52, 0x55, 0x4e, 0x45, 0x10, 0x0c,
|
||||
0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x41, 0x42, 0x4c,
|
||||
0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x0d, 0x42, 0x07, 0x0a, 0x05, 0x5f,
|
||||
0x74, 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x49, 0x44, 0x42,
|
||||
0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x11, 0x0a,
|
||||
0x0f, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x64, 0x65, 0x6c, 0x69,
|
||||
0x76, 0x65, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x61,
|
||||
0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76,
|
||||
0x65, 0x50, 0x65, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x52, 0x50,
|
||||
0x43, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x52, 0x50, 0x43, 0x42, 0x0a, 0x0a,
|
||||
0x08, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x52, 0x50, 0x43, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6a, 0x6f,
|
||||
0x69, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x42, 0x08, 0x0a, 0x06,
|
||||
0x5f, 0x67, 0x72, 0x61, 0x66, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x75, 0x6e, 0x65,
|
||||
0x42, 0x17, 0x0a, 0x15, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x61, 0x62,
|
||||
0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x42, 0x0a, 0x0f, 0x54, 0x72, 0x61,
|
||||
0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2f, 0x0a, 0x05,
|
||||
0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x6c,
|
||||
0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x63,
|
||||
0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x42, 0x43, 0x5a,
|
||||
0x41, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69,
|
||||
0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x71, 0x75, 0x69, 0x6c, 0x69, 0x62, 0x72, 0x69, 0x75,
|
||||
0x6d, 0x2f, 0x6d, 0x6f, 0x6e, 0x6f, 0x72, 0x65, 0x70, 0x6f, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69,
|
||||
0x62, 0x70, 0x32, 0x70, 0x2d, 0x62, 0x6c, 0x6f, 0x73, 0x73, 0x6f, 0x6d, 0x73, 0x75, 0x62, 0x2f,
|
||||
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1761,32 +1856,33 @@ func file_trace_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_trace_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
|
||||
var file_trace_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
|
||||
var file_trace_proto_goTypes = []interface{}{
|
||||
(TraceEvent_Type)(0), // 0: blossomsub.pb.TraceEvent.Type
|
||||
(*TraceEvent)(nil), // 1: blossomsub.pb.TraceEvent
|
||||
(*TraceEventBatch)(nil), // 2: blossomsub.pb.TraceEventBatch
|
||||
(*TraceEvent_PublishMessage)(nil), // 3: blossomsub.pb.TraceEvent.PublishMessage
|
||||
(*TraceEvent_RejectMessage)(nil), // 4: blossomsub.pb.TraceEvent.RejectMessage
|
||||
(*TraceEvent_DuplicateMessage)(nil), // 5: blossomsub.pb.TraceEvent.DuplicateMessage
|
||||
(*TraceEvent_DeliverMessage)(nil), // 6: blossomsub.pb.TraceEvent.DeliverMessage
|
||||
(*TraceEvent_AddPeer)(nil), // 7: blossomsub.pb.TraceEvent.AddPeer
|
||||
(*TraceEvent_RemovePeer)(nil), // 8: blossomsub.pb.TraceEvent.RemovePeer
|
||||
(*TraceEvent_RecvRPC)(nil), // 9: blossomsub.pb.TraceEvent.RecvRPC
|
||||
(*TraceEvent_SendRPC)(nil), // 10: blossomsub.pb.TraceEvent.SendRPC
|
||||
(*TraceEvent_DropRPC)(nil), // 11: blossomsub.pb.TraceEvent.DropRPC
|
||||
(*TraceEvent_Join)(nil), // 12: blossomsub.pb.TraceEvent.Join
|
||||
(*TraceEvent_Leave)(nil), // 13: blossomsub.pb.TraceEvent.Leave
|
||||
(*TraceEvent_Graft)(nil), // 14: blossomsub.pb.TraceEvent.Graft
|
||||
(*TraceEvent_Prune)(nil), // 15: blossomsub.pb.TraceEvent.Prune
|
||||
(*TraceEvent_RPCMeta)(nil), // 16: blossomsub.pb.TraceEvent.RPCMeta
|
||||
(*TraceEvent_MessageMeta)(nil), // 17: blossomsub.pb.TraceEvent.MessageMeta
|
||||
(*TraceEvent_SubMeta)(nil), // 18: blossomsub.pb.TraceEvent.SubMeta
|
||||
(*TraceEvent_ControlMeta)(nil), // 19: blossomsub.pb.TraceEvent.ControlMeta
|
||||
(*TraceEvent_ControlIHaveMeta)(nil), // 20: blossomsub.pb.TraceEvent.ControlIHaveMeta
|
||||
(*TraceEvent_ControlIWantMeta)(nil), // 21: blossomsub.pb.TraceEvent.ControlIWantMeta
|
||||
(*TraceEvent_ControlGraftMeta)(nil), // 22: blossomsub.pb.TraceEvent.ControlGraftMeta
|
||||
(*TraceEvent_ControlPruneMeta)(nil), // 23: blossomsub.pb.TraceEvent.ControlPruneMeta
|
||||
(TraceEvent_Type)(0), // 0: blossomsub.pb.TraceEvent.Type
|
||||
(*TraceEvent)(nil), // 1: blossomsub.pb.TraceEvent
|
||||
(*TraceEventBatch)(nil), // 2: blossomsub.pb.TraceEventBatch
|
||||
(*TraceEvent_PublishMessage)(nil), // 3: blossomsub.pb.TraceEvent.PublishMessage
|
||||
(*TraceEvent_RejectMessage)(nil), // 4: blossomsub.pb.TraceEvent.RejectMessage
|
||||
(*TraceEvent_DuplicateMessage)(nil), // 5: blossomsub.pb.TraceEvent.DuplicateMessage
|
||||
(*TraceEvent_DeliverMessage)(nil), // 6: blossomsub.pb.TraceEvent.DeliverMessage
|
||||
(*TraceEvent_AddPeer)(nil), // 7: blossomsub.pb.TraceEvent.AddPeer
|
||||
(*TraceEvent_RemovePeer)(nil), // 8: blossomsub.pb.TraceEvent.RemovePeer
|
||||
(*TraceEvent_RecvRPC)(nil), // 9: blossomsub.pb.TraceEvent.RecvRPC
|
||||
(*TraceEvent_SendRPC)(nil), // 10: blossomsub.pb.TraceEvent.SendRPC
|
||||
(*TraceEvent_DropRPC)(nil), // 11: blossomsub.pb.TraceEvent.DropRPC
|
||||
(*TraceEvent_Join)(nil), // 12: blossomsub.pb.TraceEvent.Join
|
||||
(*TraceEvent_Leave)(nil), // 13: blossomsub.pb.TraceEvent.Leave
|
||||
(*TraceEvent_Graft)(nil), // 14: blossomsub.pb.TraceEvent.Graft
|
||||
(*TraceEvent_Prune)(nil), // 15: blossomsub.pb.TraceEvent.Prune
|
||||
(*TraceEvent_UndeliverableMessage)(nil), // 16: blossomsub.pb.TraceEvent.UndeliverableMessage
|
||||
(*TraceEvent_RPCMeta)(nil), // 17: blossomsub.pb.TraceEvent.RPCMeta
|
||||
(*TraceEvent_MessageMeta)(nil), // 18: blossomsub.pb.TraceEvent.MessageMeta
|
||||
(*TraceEvent_SubMeta)(nil), // 19: blossomsub.pb.TraceEvent.SubMeta
|
||||
(*TraceEvent_ControlMeta)(nil), // 20: blossomsub.pb.TraceEvent.ControlMeta
|
||||
(*TraceEvent_ControlIHaveMeta)(nil), // 21: blossomsub.pb.TraceEvent.ControlIHaveMeta
|
||||
(*TraceEvent_ControlIWantMeta)(nil), // 22: blossomsub.pb.TraceEvent.ControlIWantMeta
|
||||
(*TraceEvent_ControlGraftMeta)(nil), // 23: blossomsub.pb.TraceEvent.ControlGraftMeta
|
||||
(*TraceEvent_ControlPruneMeta)(nil), // 24: blossomsub.pb.TraceEvent.ControlPruneMeta
|
||||
}
|
||||
var file_trace_proto_depIdxs = []int32{
|
||||
0, // 0: blossomsub.pb.TraceEvent.type:type_name -> blossomsub.pb.TraceEvent.Type
|
||||
@ -1803,22 +1899,23 @@ var file_trace_proto_depIdxs = []int32{
|
||||
13, // 11: blossomsub.pb.TraceEvent.leave:type_name -> blossomsub.pb.TraceEvent.Leave
|
||||
14, // 12: blossomsub.pb.TraceEvent.graft:type_name -> blossomsub.pb.TraceEvent.Graft
|
||||
15, // 13: blossomsub.pb.TraceEvent.prune:type_name -> blossomsub.pb.TraceEvent.Prune
|
||||
1, // 14: blossomsub.pb.TraceEventBatch.batch:type_name -> blossomsub.pb.TraceEvent
|
||||
16, // 15: blossomsub.pb.TraceEvent.RecvRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
16, // 16: blossomsub.pb.TraceEvent.SendRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
16, // 17: blossomsub.pb.TraceEvent.DropRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
17, // 18: blossomsub.pb.TraceEvent.RPCMeta.messages:type_name -> blossomsub.pb.TraceEvent.MessageMeta
|
||||
18, // 19: blossomsub.pb.TraceEvent.RPCMeta.subscription:type_name -> blossomsub.pb.TraceEvent.SubMeta
|
||||
19, // 20: blossomsub.pb.TraceEvent.RPCMeta.control:type_name -> blossomsub.pb.TraceEvent.ControlMeta
|
||||
20, // 21: blossomsub.pb.TraceEvent.ControlMeta.ihave:type_name -> blossomsub.pb.TraceEvent.ControlIHaveMeta
|
||||
21, // 22: blossomsub.pb.TraceEvent.ControlMeta.iwant:type_name -> blossomsub.pb.TraceEvent.ControlIWantMeta
|
||||
22, // 23: blossomsub.pb.TraceEvent.ControlMeta.graft:type_name -> blossomsub.pb.TraceEvent.ControlGraftMeta
|
||||
23, // 24: blossomsub.pb.TraceEvent.ControlMeta.prune:type_name -> blossomsub.pb.TraceEvent.ControlPruneMeta
|
||||
25, // [25:25] is the sub-list for method output_type
|
||||
25, // [25:25] is the sub-list for method input_type
|
||||
25, // [25:25] is the sub-list for extension type_name
|
||||
25, // [25:25] is the sub-list for extension extendee
|
||||
0, // [0:25] is the sub-list for field type_name
|
||||
16, // 14: blossomsub.pb.TraceEvent.undeliverableMessage:type_name -> blossomsub.pb.TraceEvent.UndeliverableMessage
|
||||
1, // 15: blossomsub.pb.TraceEventBatch.batch:type_name -> blossomsub.pb.TraceEvent
|
||||
17, // 16: blossomsub.pb.TraceEvent.RecvRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
17, // 17: blossomsub.pb.TraceEvent.SendRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
17, // 18: blossomsub.pb.TraceEvent.DropRPC.meta:type_name -> blossomsub.pb.TraceEvent.RPCMeta
|
||||
18, // 19: blossomsub.pb.TraceEvent.RPCMeta.messages:type_name -> blossomsub.pb.TraceEvent.MessageMeta
|
||||
19, // 20: blossomsub.pb.TraceEvent.RPCMeta.subscription:type_name -> blossomsub.pb.TraceEvent.SubMeta
|
||||
20, // 21: blossomsub.pb.TraceEvent.RPCMeta.control:type_name -> blossomsub.pb.TraceEvent.ControlMeta
|
||||
21, // 22: blossomsub.pb.TraceEvent.ControlMeta.ihave:type_name -> blossomsub.pb.TraceEvent.ControlIHaveMeta
|
||||
22, // 23: blossomsub.pb.TraceEvent.ControlMeta.iwant:type_name -> blossomsub.pb.TraceEvent.ControlIWantMeta
|
||||
23, // 24: blossomsub.pb.TraceEvent.ControlMeta.graft:type_name -> blossomsub.pb.TraceEvent.ControlGraftMeta
|
||||
24, // 25: blossomsub.pb.TraceEvent.ControlMeta.prune:type_name -> blossomsub.pb.TraceEvent.ControlPruneMeta
|
||||
26, // [26:26] is the sub-list for method output_type
|
||||
26, // [26:26] is the sub-list for method input_type
|
||||
26, // [26:26] is the sub-list for extension type_name
|
||||
26, // [26:26] is the sub-list for extension extendee
|
||||
0, // [0:26] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_trace_proto_init() }
|
||||
@ -2008,7 +2105,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_RPCMeta); i {
|
||||
switch v := v.(*TraceEvent_UndeliverableMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2020,7 +2117,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_MessageMeta); i {
|
||||
switch v := v.(*TraceEvent_RPCMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2032,7 +2129,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_SubMeta); i {
|
||||
switch v := v.(*TraceEvent_MessageMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2044,7 +2141,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlMeta); i {
|
||||
switch v := v.(*TraceEvent_SubMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2056,7 +2153,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlIHaveMeta); i {
|
||||
switch v := v.(*TraceEvent_ControlMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2068,7 +2165,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlIWantMeta); i {
|
||||
switch v := v.(*TraceEvent_ControlIHaveMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2080,7 +2177,7 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlGraftMeta); i {
|
||||
switch v := v.(*TraceEvent_ControlIWantMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
@ -2092,6 +2189,18 @@ func file_trace_proto_init() {
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlGraftMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_trace_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TraceEvent_ControlPruneMeta); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -2121,16 +2230,17 @@ func file_trace_proto_init() {
|
||||
file_trace_proto_msgTypes[15].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[16].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[17].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[19].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[21].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[18].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[20].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[22].OneofWrappers = []interface{}{}
|
||||
file_trace_proto_msgTypes[23].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_trace_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 23,
|
||||
NumMessages: 24,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
@ -22,6 +22,7 @@ message TraceEvent {
|
||||
optional Leave leave = 14;
|
||||
optional Graft graft = 15;
|
||||
optional Prune prune = 16;
|
||||
optional UndeliverableMessage undeliverableMessage = 17;
|
||||
|
||||
enum Type {
|
||||
PUBLISH_MESSAGE = 0;
|
||||
@ -37,6 +38,7 @@ message TraceEvent {
|
||||
LEAVE = 10;
|
||||
GRAFT = 11;
|
||||
PRUNE = 12;
|
||||
UNDELIVERABLE_MESSAGE = 13;
|
||||
}
|
||||
|
||||
message PublishMessage {
|
||||
@ -105,6 +107,12 @@ message TraceEvent {
|
||||
optional bytes bitmask = 2;
|
||||
}
|
||||
|
||||
message UndeliverableMessage {
|
||||
optional bytes messageID = 1;
|
||||
optional bytes bitmask = 2;
|
||||
optional bytes receivedFrom = 3;
|
||||
}
|
||||
|
||||
message RPCMeta {
|
||||
repeated MessageMeta messages = 1;
|
||||
repeated SubMeta subscription = 2;
|
||||
|
@ -204,13 +204,12 @@ func newPeerGater(ctx context.Context, host host.Host, params *PeerGaterParams)
|
||||
func (pg *peerGater) background(ctx context.Context) {
|
||||
tick := time.NewTicker(pg.params.DecayInterval)
|
||||
|
||||
defer tick.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
pg.decayStats()
|
||||
case <-ctx.Done():
|
||||
tick.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -218,7 +217,6 @@ func (pg *peerGater) background(ctx context.Context) {
|
||||
|
||||
func (pg *peerGater) decayStats() {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
pg.validate *= pg.params.GlobalDecay
|
||||
if pg.validate < pg.params.DecayToZero {
|
||||
@ -256,6 +254,8 @@ func (pg *peerGater) decayStats() {
|
||||
delete(pg.ipStats, ip)
|
||||
}
|
||||
}
|
||||
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) getPeerStats(p peer.ID) *peerGaterStats {
|
||||
@ -323,21 +323,23 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
|
||||
}
|
||||
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
// check the quiet period; if the validation queue has not throttled for more than the Quiet
|
||||
// interval, we turn off the circuit breaker and accept.
|
||||
if time.Since(pg.lastThrottle) > pg.params.Quiet {
|
||||
pg.Unlock()
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
// no throttle events -- or they have decayed; accept.
|
||||
if pg.throttle == 0 {
|
||||
pg.Unlock()
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
// check the throttle/validate ration; if it is below threshold we accept.
|
||||
if pg.validate != 0 && pg.throttle/pg.validate < pg.params.Threshold {
|
||||
pg.Unlock()
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
@ -346,6 +348,7 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
|
||||
// compute the goodput of the peer; the denominator is the weighted mix of message counters
|
||||
total := st.deliver + pg.params.DuplicateWeight*st.duplicate + pg.params.IgnoreWeight*st.ignore + pg.params.RejectWeight*st.reject
|
||||
if total == 0 {
|
||||
pg.Unlock()
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
@ -355,10 +358,12 @@ func (pg *peerGater) AcceptFrom(p peer.ID) AcceptStatus {
|
||||
// accepted; this is not a sinkhole/blacklist.
|
||||
threshold := (1 + st.deliver) / (1 + total)
|
||||
if rand.Float64() < threshold {
|
||||
pg.Unlock()
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
log.Debugf("throttling peer %s with threshold %f", p, threshold)
|
||||
pg.Unlock()
|
||||
return AcceptControl
|
||||
}
|
||||
|
||||
@ -368,21 +373,21 @@ var _ RawTracer = (*peerGater)(nil)
|
||||
// tracer interface
|
||||
func (pg *peerGater) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
st := pg.getPeerStats(p)
|
||||
st.connected++
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) RemovePeer(p peer.ID) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
st := pg.getPeerStats(p)
|
||||
st.connected--
|
||||
st.expire = time.Now().Add(pg.params.RetainStats)
|
||||
|
||||
delete(pg.peerStats, p)
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) Join(bitmask []byte) {}
|
||||
@ -392,14 +397,13 @@ func (pg *peerGater) Prune(p peer.ID, bitmask []byte) {}
|
||||
|
||||
func (pg *peerGater) ValidateMessage(msg *Message) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
pg.validate++
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) DeliverMessage(msg *Message) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
st := pg.getPeerStats(msg.ReceivedFrom)
|
||||
|
||||
@ -411,11 +415,11 @@ func (pg *peerGater) DeliverMessage(msg *Message) {
|
||||
}
|
||||
|
||||
st.deliver += weight
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) RejectMessage(msg *Message, reason string) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
switch reason {
|
||||
case RejectValidationQueueFull:
|
||||
@ -432,14 +436,15 @@ func (pg *peerGater) RejectMessage(msg *Message, reason string) {
|
||||
st := pg.getPeerStats(msg.ReceivedFrom)
|
||||
st.reject++
|
||||
}
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) DuplicateMessage(msg *Message) {
|
||||
pg.Lock()
|
||||
defer pg.Unlock()
|
||||
|
||||
st := pg.getPeerStats(msg.ReceivedFrom)
|
||||
st.duplicate++
|
||||
pg.Unlock()
|
||||
}
|
||||
|
||||
func (pg *peerGater) ThrottlePeer(p peer.ID) {}
|
||||
|
112
go-libp2p-blossomsub/peer_notify.go
Normal file
112
go-libp2p-blossomsub/peer_notify.go
Normal file
@ -0,0 +1,112 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/event"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
)
|
||||
|
||||
func (ps *PubSub) watchForNewPeers(ctx context.Context) {
|
||||
// We don't bother subscribing to "connectivity" events because we always run identify after
|
||||
// every new connection.
|
||||
sub, err := ps.host.EventBus().Subscribe([]interface{}{
|
||||
&event.EvtPeerIdentificationCompleted{},
|
||||
&event.EvtPeerProtocolsUpdated{},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("failed to subscribe to peer identification events: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ps.newPeersPrioLk.RLock()
|
||||
ps.newPeersMx.Lock()
|
||||
for _, pid := range ps.host.Network().Peers() {
|
||||
if ps.host.Network().Connectedness(pid) != network.Connected {
|
||||
continue
|
||||
}
|
||||
ps.newPeersPend[pid] = struct{}{}
|
||||
}
|
||||
ps.newPeersMx.Unlock()
|
||||
ps.newPeersPrioLk.RUnlock()
|
||||
|
||||
select {
|
||||
case ps.newPeers <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
var supportsProtocol func(protocol.ID) bool
|
||||
if ps.protoMatchFunc != nil {
|
||||
var supportedProtocols []func(protocol.ID) bool
|
||||
for _, proto := range ps.rt.Protocols() {
|
||||
|
||||
supportedProtocols = append(supportedProtocols, ps.protoMatchFunc(proto))
|
||||
}
|
||||
supportsProtocol = func(proto protocol.ID) bool {
|
||||
for _, fn := range supportedProtocols {
|
||||
if (fn)(proto) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
supportedProtocols := make(map[protocol.ID]struct{})
|
||||
for _, proto := range ps.rt.Protocols() {
|
||||
supportedProtocols[proto] = struct{}{}
|
||||
}
|
||||
supportsProtocol = func(proto protocol.ID) bool {
|
||||
_, ok := supportedProtocols[proto]
|
||||
return ok
|
||||
}
|
||||
}
|
||||
|
||||
for ctx.Err() == nil {
|
||||
var ev any
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
sub.Close()
|
||||
return
|
||||
case ev = <-sub.Out():
|
||||
}
|
||||
|
||||
var protos []protocol.ID
|
||||
var peer peer.ID
|
||||
switch ev := ev.(type) {
|
||||
case event.EvtPeerIdentificationCompleted:
|
||||
peer = ev.Peer
|
||||
protos = ev.Protocols
|
||||
case event.EvtPeerProtocolsUpdated:
|
||||
peer = ev.Peer
|
||||
protos = ev.Added
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't bother checking connectivity (connected and non-"limited") here because
|
||||
// we'll check when actually handling the new peer.
|
||||
|
||||
for _, p := range protos {
|
||||
if supportsProtocol(p) {
|
||||
ps.notifyNewPeer(peer)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sub.Close()
|
||||
}
|
||||
|
||||
func (ps *PubSub) notifyNewPeer(peer peer.ID) {
|
||||
ps.newPeersPrioLk.RLock()
|
||||
ps.newPeersMx.Lock()
|
||||
ps.newPeersPend[peer] = struct{}{}
|
||||
ps.newPeersMx.Unlock()
|
||||
ps.newPeersPrioLk.RUnlock()
|
||||
|
||||
select {
|
||||
case ps.newPeers <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
@ -1,11 +1,14 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -23,8 +26,8 @@ import (
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
)
|
||||
|
||||
// DefaultMaximumMessageSize is 16.7 MB.
|
||||
const DefaultMaxMessageSize = 1 << 24
|
||||
// DefaultMaximumMessageSize is 1 MB.
|
||||
const DefaultMaxMessageSize = 1 << 20
|
||||
|
||||
var (
|
||||
// TimeCacheDuration specifies how long a message ID will be remembered as seen.
|
||||
@ -147,7 +150,8 @@ type PubSub struct {
|
||||
blacklist Blacklist
|
||||
blacklistPeer chan peer.ID
|
||||
|
||||
peers map[peer.ID]chan *RPC
|
||||
peers map[peer.ID]chan *RPC
|
||||
peersMx sync.RWMutex
|
||||
|
||||
inboundStreamsMx sync.Mutex
|
||||
inboundStreams map[peer.ID]network.Stream
|
||||
@ -231,7 +235,7 @@ const (
|
||||
|
||||
type Message struct {
|
||||
*pb.Message
|
||||
ID string
|
||||
ID []byte
|
||||
ReceivedFrom peer.ID
|
||||
ValidatorData interface{}
|
||||
Local bool
|
||||
@ -242,7 +246,7 @@ func (m *Message) GetFrom() peer.ID {
|
||||
}
|
||||
|
||||
type RPC struct {
|
||||
pb.RPC
|
||||
*pb.RPC
|
||||
|
||||
// unexported on purpose, not sending this over the wire
|
||||
from peer.ID
|
||||
@ -263,7 +267,7 @@ func NewPubSub(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option
|
||||
peerOutboundQueueSize: 32,
|
||||
signID: h.ID(),
|
||||
signKey: nil,
|
||||
signPolicy: StrictSign,
|
||||
signPolicy: LaxSign,
|
||||
incoming: make(chan *RPC, 32),
|
||||
newPeers: make(chan struct{}, 1),
|
||||
newPeersPend: make(map[peer.ID]struct{}),
|
||||
@ -330,20 +334,19 @@ func NewPubSub(ctx context.Context, h host.Host, rt PubSubRouter, opts ...Option
|
||||
h.SetStreamHandler(id, ps.handleNewStream)
|
||||
}
|
||||
}
|
||||
h.Network().Notify((*PubSubNotif)(ps))
|
||||
|
||||
go ps.watchForNewPeers(ctx)
|
||||
|
||||
ps.val.Start(ps)
|
||||
|
||||
go ps.processLoop(ctx)
|
||||
|
||||
(*PubSubNotif)(ps).Initialize()
|
||||
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
// MsgIdFunction returns a unique ID for the passed Message, and PubSub can be customized to use any
|
||||
// implementation of this function by configuring it with the Option from WithMessageIdFn.
|
||||
type MsgIdFunction func(pmsg *pb.Message) string
|
||||
type MsgIdFunction func(pmsg *pb.Message) []byte
|
||||
|
||||
// WithMessageIdFn is an option to customize the way a message ID is computed for a pubsub message.
|
||||
// The default ID function is DefaultMsgIdFn (concatenate source and seq nr.),
|
||||
@ -495,14 +498,14 @@ func WithRawTracer(tracer RawTracer) Option {
|
||||
}
|
||||
|
||||
// WithMaxMessageSize sets the global maximum message size for pubsub wire
|
||||
// messages. The default value is 16.7MiB (DefaultMaxMessageSize).
|
||||
// messages. The default value is 1MiB (DefaultMaxMessageSize).
|
||||
//
|
||||
// Observe the following warnings when setting this option.
|
||||
//
|
||||
// WARNING #1: Make sure to change the default protocol prefixes for floodsub
|
||||
// (FloodSubID) and BlossomSub (BlossomSubID). This avoids accidentally joining
|
||||
// the public default network, which uses the default max message size, and
|
||||
// therefore will cause messages to be dropped.
|
||||
// WARNING #1: Make sure to change the default protocol prefixes for BlossomSub
|
||||
// (BlossomSubID). This avoids accidentally joining the public default network,
|
||||
// which uses the default max message size, and therefore will cause messages to
|
||||
// be dropped.
|
||||
//
|
||||
// WARNING #2: Reducing the default max message limit is fine, if you are
|
||||
// certain that your application messages will not exceed the new limit.
|
||||
@ -563,11 +566,13 @@ func WithAppSpecificRpcInspector(inspector func(peer.ID, *RPC) error) Option {
|
||||
// processLoop handles all inputs arriving on the channels
|
||||
func (p *PubSub) processLoop(ctx context.Context) {
|
||||
defer func() {
|
||||
p.peersMx.Lock()
|
||||
// Clean up go routines.
|
||||
for _, ch := range p.peers {
|
||||
close(ch)
|
||||
}
|
||||
p.peers = nil
|
||||
p.peersMx.Unlock()
|
||||
p.bitmasks = nil
|
||||
p.seenMessages.Done()
|
||||
}()
|
||||
@ -580,7 +585,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
case s := <-p.newPeerStream:
|
||||
pid := s.Conn().RemotePeer()
|
||||
|
||||
p.peersMx.RLock()
|
||||
ch, ok := p.peers[pid]
|
||||
p.peersMx.RUnlock()
|
||||
if !ok {
|
||||
log.Warn("new stream for unknown peer: ", pid)
|
||||
s.Reset()
|
||||
@ -590,7 +597,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
if p.blacklist.Contains(pid) {
|
||||
log.Warn("closing stream for blacklisted peer: ", pid)
|
||||
close(ch)
|
||||
p.peersMx.Lock()
|
||||
delete(p.peers, pid)
|
||||
p.peersMx.Unlock()
|
||||
s.Reset()
|
||||
continue
|
||||
}
|
||||
@ -598,7 +607,9 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
p.rt.AddPeer(pid, s.Protocol())
|
||||
|
||||
case pid := <-p.newPeerError:
|
||||
p.peersMx.Lock()
|
||||
delete(p.peers, pid)
|
||||
p.peersMx.Unlock()
|
||||
|
||||
case <-p.peerDead:
|
||||
p.handleDeadPeers()
|
||||
@ -622,22 +633,13 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
case bitmask := <-p.rmRelay:
|
||||
p.handleRemoveRelay([]byte(bitmask))
|
||||
case preq := <-p.getPeers:
|
||||
tmap, ok := p.bitmasks[string(preq.bitmask)]
|
||||
if preq.bitmask != nil && !ok {
|
||||
peers := p.getPeersInBitmask(preq.bitmask)
|
||||
|
||||
if len(peers) == 0 {
|
||||
preq.resp <- nil
|
||||
continue
|
||||
} else {
|
||||
preq.resp <- peers
|
||||
}
|
||||
var peers []peer.ID
|
||||
for p := range p.peers {
|
||||
if preq.bitmask != nil {
|
||||
_, ok := tmap[p]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
peers = append(peers, p)
|
||||
}
|
||||
preq.resp <- peers
|
||||
case rpc := <-p.incoming:
|
||||
p.handleIncomingRPC(rpc)
|
||||
|
||||
@ -657,10 +659,14 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
log.Infof("Blacklisting peer %s", pid)
|
||||
p.blacklist.Add(pid)
|
||||
|
||||
p.peersMx.RLock()
|
||||
ch, ok := p.peers[pid]
|
||||
p.peersMx.RUnlock()
|
||||
if ok {
|
||||
close(ch)
|
||||
p.peersMx.Lock()
|
||||
delete(p.peers, pid)
|
||||
p.peersMx.Unlock()
|
||||
for t, tmap := range p.bitmasks {
|
||||
if _, ok := tmap[pid]; ok {
|
||||
delete(tmap, pid)
|
||||
@ -677,6 +683,49 @@ func (p *PubSub) processLoop(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PubSub) getPeersInBitmask(bitmask []byte) []peer.ID {
|
||||
bitmaskSlices := SliceBitmask(bitmask)
|
||||
var peers []peer.ID
|
||||
peerloop:
|
||||
for _, slice := range bitmaskSlices {
|
||||
tmap, ok := p.bitmasks[string(slice)]
|
||||
if !ok {
|
||||
peers = []peer.ID{}
|
||||
break peerloop
|
||||
}
|
||||
|
||||
var peerset []peer.ID
|
||||
p.peersMx.RLock()
|
||||
for p := range p.peers {
|
||||
_, ok := tmap[p]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
peerset = append(peerset, p)
|
||||
}
|
||||
p.peersMx.RUnlock()
|
||||
|
||||
if len(peers) == 0 {
|
||||
peers = peerset
|
||||
} else {
|
||||
var update []peer.ID
|
||||
for _, p := range peers {
|
||||
if slices.Contains(peerset, p) {
|
||||
update = append(update, p)
|
||||
}
|
||||
}
|
||||
|
||||
peers = update
|
||||
|
||||
if len(update) == 0 {
|
||||
break peerloop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
func (p *PubSub) handlePendingPeers() {
|
||||
p.newPeersPrioLk.Lock()
|
||||
|
||||
@ -694,10 +743,13 @@ func (p *PubSub) handlePendingPeers() {
|
||||
continue
|
||||
}
|
||||
|
||||
p.peersMx.RLock()
|
||||
if _, ok := p.peers[pid]; ok {
|
||||
p.peersMx.RUnlock()
|
||||
log.Debug("already have connection to peer: ", pid)
|
||||
continue
|
||||
}
|
||||
p.peersMx.RUnlock()
|
||||
|
||||
if p.blacklist.Contains(pid) {
|
||||
log.Warn("ignoring connection from blacklisted peer: ", pid)
|
||||
@ -707,7 +759,9 @@ func (p *PubSub) handlePendingPeers() {
|
||||
messages := make(chan *RPC, p.peerOutboundQueueSize)
|
||||
messages <- p.getHelloPacket()
|
||||
go p.handleNewPeer(p.ctx, pid, messages)
|
||||
p.peersMx.Lock()
|
||||
p.peers[pid] = messages
|
||||
p.peersMx.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@ -724,13 +778,17 @@ func (p *PubSub) handleDeadPeers() {
|
||||
p.peerDeadPrioLk.Unlock()
|
||||
|
||||
for pid := range deadPeers {
|
||||
p.peersMx.RLock()
|
||||
ch, ok := p.peers[pid]
|
||||
p.peersMx.RUnlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
close(ch)
|
||||
p.peersMx.Lock()
|
||||
delete(p.peers, pid)
|
||||
p.peersMx.Unlock()
|
||||
|
||||
for t, tmap := range p.bitmasks {
|
||||
if _, ok := tmap[pid]; ok {
|
||||
@ -753,7 +811,9 @@ func (p *PubSub) handleDeadPeers() {
|
||||
log.Debugf("peer declared dead but still connected; respawning writer: %s", pid)
|
||||
messages := make(chan *RPC, p.peerOutboundQueueSize)
|
||||
messages <- p.getHelloPacket()
|
||||
p.peersMx.Lock()
|
||||
p.peers[pid] = messages
|
||||
p.peersMx.Unlock()
|
||||
go p.handleNewPeerWithBackoff(p.ctx, pid, backoffDelay, messages)
|
||||
}
|
||||
}
|
||||
@ -917,6 +977,7 @@ func (p *PubSub) announce(bitmask []byte, sub bool) {
|
||||
}
|
||||
|
||||
out := rpcWithSubs(subopt)
|
||||
p.peersMx.RLock()
|
||||
for pid, peer := range p.peers {
|
||||
select {
|
||||
case peer <- out:
|
||||
@ -927,6 +988,7 @@ func (p *PubSub) announce(bitmask []byte, sub bool) {
|
||||
go p.announceRetry(pid, bitmask, sub)
|
||||
}
|
||||
}
|
||||
p.peersMx.RUnlock()
|
||||
}
|
||||
|
||||
func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
|
||||
@ -950,7 +1012,9 @@ func (p *PubSub) announceRetry(pid peer.ID, bitmask []byte, sub bool) {
|
||||
}
|
||||
|
||||
func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
|
||||
p.peersMx.RLock()
|
||||
peer, ok := p.peers[pid]
|
||||
p.peersMx.RUnlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
@ -975,14 +1039,13 @@ func (p *PubSub) doAnnounceRetry(pid peer.ID, bitmask []byte, sub bool) {
|
||||
// Only called from processLoop.
|
||||
func (p *PubSub) notifySubs(msg *Message) {
|
||||
bitmask := msg.GetBitmask()
|
||||
subs := p.mySubs[string(bitmask)]
|
||||
slices := SliceBitmask(bitmask)
|
||||
// o := rand.Intn(len(slices))
|
||||
subs := p.mySubs[string(slices[0])]
|
||||
for f := range subs {
|
||||
select {
|
||||
case f.ch <- msg:
|
||||
case <-time.After(5 * time.Millisecond):
|
||||
// it's unreasonable to immediately fall over because a subscriber didn't
|
||||
// answer, message delivery sometimes lands next nanosecond and dropping
|
||||
// it when there's room is absurd.
|
||||
default:
|
||||
p.tracer.UndeliverableMessage(msg)
|
||||
log.Infof("Can't deliver message to subscription for bitmask %x; subscriber too slow", bitmask)
|
||||
}
|
||||
@ -990,14 +1053,14 @@ func (p *PubSub) notifySubs(msg *Message) {
|
||||
}
|
||||
|
||||
// seenMessage returns whether we already saw this message before
|
||||
func (p *PubSub) seenMessage(id string) bool {
|
||||
return p.seenMessages.Has(id)
|
||||
func (p *PubSub) seenMessage(id []byte) bool {
|
||||
return p.seenMessages.Has(string(id))
|
||||
}
|
||||
|
||||
// markSeen marks a message as seen such that seenMessage returns `true' for the given id
|
||||
// returns true if the message was freshly marked
|
||||
func (p *PubSub) markSeen(id string) bool {
|
||||
return p.seenMessages.Add(id)
|
||||
func (p *PubSub) markSeen(id []byte) bool {
|
||||
return p.seenMessages.Add(string(id))
|
||||
}
|
||||
|
||||
// subscribedToMessage returns whether we are subscribed to one of the bitmasks
|
||||
@ -1008,9 +1071,15 @@ func (p *PubSub) subscribedToMsg(msg *pb.Message) bool {
|
||||
}
|
||||
|
||||
bitmask := msg.GetBitmask()
|
||||
_, ok := p.mySubs[string(bitmask)]
|
||||
slices := SliceBitmask(bitmask)
|
||||
for _, slice := range slices {
|
||||
_, ok := p.mySubs[string(slice)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return ok
|
||||
return true
|
||||
}
|
||||
|
||||
// canRelayMsg returns whether we are able to relay for one of the bitmasks
|
||||
@ -1021,9 +1090,15 @@ func (p *PubSub) canRelayMsg(msg *pb.Message) bool {
|
||||
}
|
||||
|
||||
bitmask := msg.GetBitmask()
|
||||
relays := p.myRelays[string(bitmask)]
|
||||
slices := SliceBitmask(bitmask)
|
||||
for _, slice := range slices {
|
||||
relays := p.myRelays[string(slice)]
|
||||
if relays > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return relays > 0
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *PubSub) notifyLeave(bitmask []byte, pid peer.ID) {
|
||||
@ -1049,7 +1124,7 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
|
||||
var err error
|
||||
subs, err = p.subFilter.FilterIncomingSubscriptions(rpc.from, subs)
|
||||
if err != nil {
|
||||
log.Debugf("subscription filter error: %s; ignoring RPC", err)
|
||||
log.Debugf("subscription filter error: %s; ignoring RPC\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -1103,7 +1178,7 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
|
||||
continue
|
||||
}
|
||||
|
||||
p.pushMsg(&Message{pmsg, "", rpc.from, nil, false})
|
||||
p.pushMsg(&Message{pmsg, []byte{}, rpc.from, nil, false})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1111,8 +1186,10 @@ func (p *PubSub) handleIncomingRPC(rpc *RPC) {
|
||||
}
|
||||
|
||||
// DefaultMsgIdFn returns a unique ID of the passed Message
|
||||
func DefaultMsgIdFn(pmsg *pb.Message) string {
|
||||
return string(pmsg.GetFrom()) + string(pmsg.GetSeqno())
|
||||
func DefaultMsgIdFn(pmsg *pb.Message) []byte {
|
||||
h := sha256.New()
|
||||
h.Write(pmsg.Data)
|
||||
return h.Sum([]byte{0x01})
|
||||
}
|
||||
|
||||
// DefaultPeerFilter accepts all peers on all bitmasks
|
||||
@ -1233,59 +1310,75 @@ func (p *PubSub) PeerScore(pr peer.ID) float64 {
|
||||
return p.rt.PeerScore(pr)
|
||||
}
|
||||
|
||||
// Join joins the bitmask and returns a Bitmask handle. Only one Bitmask handle should exist per bitmask, and Join will error if
|
||||
// the Bitmask handle already exists.
|
||||
func (p *PubSub) Join(bitmask []byte, opts ...BitmaskOpt) (*Bitmask, error) {
|
||||
t, ok, err := p.tryJoin(bitmask, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Join joins the bitmasks and returns a set of Bitmask handles. Only one Bitmask
|
||||
// handle should exist per bit, and Join will error if all the Bitmask handles already exist.
|
||||
func (p *PubSub) Join(bitmask []byte, opts ...BitmaskOpt) ([]*Bitmask, error) {
|
||||
ts, news, errs := p.tryJoin(bitmask, opts...)
|
||||
if len(errs) != 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
if !slices.Contains(news, true) {
|
||||
return nil, fmt.Errorf("bitmask already exists")
|
||||
}
|
||||
|
||||
return t, nil
|
||||
return ts, nil
|
||||
}
|
||||
|
||||
// tryJoin is an internal function that tries to join a bitmask
|
||||
// Returns the bitmask if it can be created or found
|
||||
// Returns true if the bitmask was newly created, false otherwise
|
||||
// Can be removed once pubsub.Publish() and pubsub.Subscribe() are removed
|
||||
func (p *PubSub) tryJoin(bitmask []byte, opts ...BitmaskOpt) (*Bitmask, bool, error) {
|
||||
func (p *PubSub) tryJoin(bitmask []byte, opts ...BitmaskOpt) ([]*Bitmask, []bool, []error) {
|
||||
if p.subFilter != nil && !p.subFilter.CanSubscribe(bitmask) {
|
||||
return nil, false, fmt.Errorf("bitmask is not allowed by the subscription filter")
|
||||
return nil, nil, []error{fmt.Errorf("bitmask is not allowed by the subscription filter")}
|
||||
}
|
||||
|
||||
t := &Bitmask{
|
||||
p: p,
|
||||
bitmask: bitmask,
|
||||
evtHandlers: make(map[*BitmaskEventHandler]struct{}),
|
||||
}
|
||||
sliced := SliceBitmask(bitmask)
|
||||
var bitmasks []*Bitmask
|
||||
var newBitmasks []bool
|
||||
var errors []error
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(t)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
loop:
|
||||
for _, slice := range sliced {
|
||||
slice := slice
|
||||
|
||||
t := &Bitmask{
|
||||
p: p,
|
||||
bitmask: slice,
|
||||
evtHandlers: make(map[*BitmaskEventHandler]struct{}),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
err := opt(t)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
resp := make(chan *Bitmask, 1)
|
||||
select {
|
||||
case t.p.addBitmask <- &addBitmaskReq{
|
||||
bitmask: t,
|
||||
resp: resp,
|
||||
}:
|
||||
case <-t.p.ctx.Done():
|
||||
errors = append(errors, t.p.ctx.Err())
|
||||
continue loop
|
||||
}
|
||||
returnedBitmask := <-resp
|
||||
|
||||
if returnedBitmask != t {
|
||||
bitmasks = append(bitmasks, returnedBitmask)
|
||||
newBitmasks = append(newBitmasks, false)
|
||||
} else {
|
||||
bitmasks = append(bitmasks, t)
|
||||
newBitmasks = append(newBitmasks, true)
|
||||
}
|
||||
}
|
||||
|
||||
resp := make(chan *Bitmask, 1)
|
||||
select {
|
||||
case t.p.addBitmask <- &addBitmaskReq{
|
||||
bitmask: t,
|
||||
resp: resp,
|
||||
}:
|
||||
case <-t.p.ctx.Done():
|
||||
return nil, false, t.p.ctx.Err()
|
||||
}
|
||||
returnedBitmask := <-resp
|
||||
|
||||
if returnedBitmask != t {
|
||||
return returnedBitmask, false, nil
|
||||
}
|
||||
|
||||
return t, true, nil
|
||||
return bitmasks, newBitmasks, errors
|
||||
}
|
||||
|
||||
type addSubReq struct {
|
||||
@ -1300,14 +1393,24 @@ type SubOpt func(sub *Subscription) error
|
||||
// before the subscription is processed by the pubsub main loop and propagated to our peers.
|
||||
//
|
||||
// Deprecated: use pubsub.Join() and bitmask.Subscribe() instead
|
||||
func (p *PubSub) Subscribe(bitmask []byte, opts ...SubOpt) (*Subscription, error) {
|
||||
func (p *PubSub) Subscribe(bitmask []byte, opts ...SubOpt) ([]*Subscription, error) {
|
||||
// ignore whether the bitmask was newly created or not, since either way we have a valid bitmask to work with
|
||||
bitmaskHandle, _, err := p.tryJoin(bitmask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
bitmaskHandles, _, errs := p.tryJoin(bitmask)
|
||||
if len(errs) != 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return bitmaskHandle.Subscribe(opts...)
|
||||
var subs []*Subscription
|
||||
for _, handle := range bitmaskHandles {
|
||||
sub, err := handle.Subscribe(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
return subs, nil
|
||||
}
|
||||
|
||||
// WithBufferSize is a Subscribe option to customize the size of the subscribe output buffer.
|
||||
@ -1335,17 +1438,20 @@ func (p *PubSub) GetBitmasks() []string {
|
||||
return <-out
|
||||
}
|
||||
|
||||
// Publish publishes data to the given bitmask.
|
||||
//
|
||||
// Deprecated: use pubsub.Join() and bitmask.Publish() instead
|
||||
func (p *PubSub) Publish(bitmask []byte, data []byte, opts ...PubOpt) error {
|
||||
// ignore whether the bitmask was newly created or not, since either way we have a valid bitmask to work with
|
||||
t, _, err := p.tryJoin(bitmask)
|
||||
if err != nil {
|
||||
return err
|
||||
func (p *PubSub) Publish(ctx context.Context, bitmask []byte, data []byte, opts ...PubOpt) error {
|
||||
peers := p.ListPeers(bitmask)
|
||||
if len(peers) == 0 {
|
||||
return ErrBitmaskClosed
|
||||
}
|
||||
|
||||
return t.Publish(context.TODO(), data, opts...)
|
||||
slices := SliceBitmask(bitmask)
|
||||
o := rand.Intn(len(slices))
|
||||
b, _, errs := p.tryJoin(slices[o])
|
||||
if len(errs) != 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
return b[0].Publish(ctx, bitmask, data, opts...)
|
||||
}
|
||||
|
||||
func (p *PubSub) nextSeqno() []byte {
|
||||
@ -1430,3 +1536,34 @@ type addRelayReq struct {
|
||||
bitmask []byte
|
||||
resp chan RelayCancelFunc
|
||||
}
|
||||
|
||||
func SliceBitmask(bitmask []byte) [][]byte {
|
||||
sliced := [][]byte{}
|
||||
if bytes.Equal(bitmask, make([]byte, len(bitmask))) {
|
||||
sliced = append(sliced, bitmask)
|
||||
} else {
|
||||
for i, b := range bitmask {
|
||||
if b == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// fast: one bit in byte
|
||||
if b&(b-1) == 0 {
|
||||
slice := make([]byte, len(bitmask))
|
||||
slice[i] = b
|
||||
sliced = append(sliced, slice)
|
||||
continue
|
||||
}
|
||||
|
||||
for j := 7; j >= 0; j-- {
|
||||
if (b>>j)&1 == 1 {
|
||||
slice := make([]byte, len(bitmask))
|
||||
slice[i] = 1 << j
|
||||
sliced = append(sliced, slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sliced
|
||||
}
|
||||
|
@ -2,48 +2,98 @@ package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
)
|
||||
|
||||
// See https://source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/issues/426
|
||||
func TestPubSubRemovesBlacklistedPeer(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
|
||||
bl := NewMapBlacklist()
|
||||
|
||||
psubs0 := getPubsub(ctx, hosts[0])
|
||||
psubs1 := getPubsub(ctx, hosts[1], WithBlacklist(bl))
|
||||
psubs0 := getBlossomSub(ctx, hosts[0])
|
||||
psubs1 := getBlossomSub(ctx, hosts[1], WithBlacklist(bl))
|
||||
connect(t, hosts[0], hosts[1])
|
||||
|
||||
// Bad peer is blacklisted after it has connected.
|
||||
// Calling p.BlacklistPeer directly does the right thing but we should also clean
|
||||
// up the peer if it has been added the the blacklist by another means.
|
||||
bl.Add(hosts[0].ID())
|
||||
|
||||
_, err := psubs0.Subscribe([]byte{0x7e, 0x57})
|
||||
bitmasks, err := psubs0.Join([]byte{0x01, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub1, err := psubs1.Subscribe([]byte{0x7e, 0x57})
|
||||
_, err = psubs0.Subscribe([]byte{0x01, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub1, err := psubs1.Subscribe([]byte{0x01, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
||||
psubs0.Publish([]byte{0x7e, 0x57}, []byte("message"))
|
||||
bitmasks[0].Publish(ctx, []byte{0x01, 0x00}, []byte("message"))
|
||||
|
||||
wctx, cancel2 := context.WithTimeout(ctx, 1*time.Second)
|
||||
defer cancel2()
|
||||
|
||||
_, _ = sub1.Next(wctx)
|
||||
_, _ = sub1[0].Next(wctx)
|
||||
|
||||
// Explicitly cancel context so PubSub cleans up peer channels.
|
||||
// Issue 426 reports a panic due to a peer channel being closed twice.
|
||||
cancel()
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
func TestSliceBitmask(t *testing.T) {
|
||||
fullVector := []byte{
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
}
|
||||
|
||||
partialVector := []byte{
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
outputs := SliceBitmask(fullVector)
|
||||
if len(outputs) != 256 {
|
||||
t.Fatalf("output length mismatch: %d, expected %d", len(outputs), 256)
|
||||
}
|
||||
|
||||
outputs = SliceBitmask(partialVector)
|
||||
if len(outputs) != 4 {
|
||||
t.Fatalf("output length mismatch: %d, expected %d", len(outputs), 4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultMsgIdFn(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
data := make([]byte, 1024)
|
||||
rand.Read(data)
|
||||
// for v2, prepends 0x01
|
||||
out := DefaultMsgIdFn(&pb.Message{
|
||||
Data: data,
|
||||
})
|
||||
if len(out) != 33 {
|
||||
t.Fatalf("length mismatch for msg id fn: %d, expected %d\n", len(out), 33)
|
||||
}
|
||||
if out[0] != 0x01 {
|
||||
t.Fatalf("missing prefix byte for msg id fn: %x, expected %x\n", out[:1], []byte{0x01})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,172 +0,0 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
RandomSubID = protocol.ID("/randomsub/1.0.0")
|
||||
)
|
||||
|
||||
var (
|
||||
RandomSubD = 6
|
||||
)
|
||||
|
||||
// NewRandomSub returns a new PubSub object using RandomSubRouter as the router.
|
||||
func NewRandomSub(ctx context.Context, h host.Host, size int, opts ...Option) (*PubSub, error) {
|
||||
rt := &RandomSubRouter{
|
||||
size: size,
|
||||
peers: make(map[peer.ID]protocol.ID),
|
||||
}
|
||||
return NewPubSub(ctx, h, rt, opts...)
|
||||
}
|
||||
|
||||
// RandomSubRouter is a router that implements a random propagation strategy.
|
||||
// For each message, it selects the square root of the network size peers, with a min of RandomSubD,
|
||||
// and forwards the message to them.
|
||||
type RandomSubRouter struct {
|
||||
p *PubSub
|
||||
peers map[peer.ID]protocol.ID
|
||||
size int
|
||||
tracer *pubsubTracer
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) Protocols() []protocol.ID {
|
||||
return []protocol.ID{RandomSubID, FloodSubID}
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) Attach(p *PubSub) {
|
||||
rs.p = p
|
||||
rs.tracer = p.tracer
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) PeerScore(p peer.ID) float64 {
|
||||
return rs.p.PeerScore(p)
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
rs.tracer.AddPeer(p, proto)
|
||||
rs.peers[p] = proto
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) RemovePeer(p peer.ID) {
|
||||
rs.tracer.RemovePeer(p)
|
||||
delete(rs.peers, p)
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) EnoughPeers(bitmask []byte, suggested int) bool {
|
||||
// check all peers in the bitmask
|
||||
tmap, ok := rs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
fsPeers := 0
|
||||
rsPeers := 0
|
||||
|
||||
// count floodsub and randomsub peers
|
||||
for p := range tmap {
|
||||
switch rs.peers[p] {
|
||||
case FloodSubID:
|
||||
fsPeers++
|
||||
case RandomSubID:
|
||||
rsPeers++
|
||||
}
|
||||
}
|
||||
|
||||
if suggested == 0 {
|
||||
suggested = RandomSubD
|
||||
}
|
||||
|
||||
if fsPeers+rsPeers >= suggested {
|
||||
return true
|
||||
}
|
||||
|
||||
if rsPeers >= RandomSubD {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) AcceptFrom(peer.ID) AcceptStatus {
|
||||
return AcceptAll
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) HandleRPC(rpc *RPC) {}
|
||||
|
||||
func (rs *RandomSubRouter) Publish(msg *Message) {
|
||||
from := msg.ReceivedFrom
|
||||
|
||||
tosend := make(map[peer.ID]struct{})
|
||||
rspeers := make(map[peer.ID]struct{})
|
||||
src := peer.ID(msg.GetFrom())
|
||||
|
||||
bitmask := msg.GetBitmask()
|
||||
tmap, ok := rs.p.bitmasks[string(bitmask)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for p := range tmap {
|
||||
if p == from || p == src {
|
||||
continue
|
||||
}
|
||||
|
||||
if rs.peers[p] == FloodSubID {
|
||||
tosend[p] = struct{}{}
|
||||
} else {
|
||||
rspeers[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rspeers) > RandomSubD {
|
||||
target := RandomSubD
|
||||
sqrt := int(math.Ceil(math.Sqrt(float64(rs.size))))
|
||||
if sqrt > target {
|
||||
target = sqrt
|
||||
}
|
||||
if target > len(rspeers) {
|
||||
target = len(rspeers)
|
||||
}
|
||||
xpeers := peerMapToList(rspeers)
|
||||
shufflePeers(xpeers)
|
||||
xpeers = xpeers[:target]
|
||||
for _, p := range xpeers {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
for p := range rspeers {
|
||||
tosend[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
out := rpcWithMessages(msg.Message)
|
||||
for p := range tosend {
|
||||
mch, ok := rs.p.peers[p]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case mch <- out:
|
||||
rs.tracer.SendRPC(out, p)
|
||||
default:
|
||||
log.Infof("dropping message to peer %s: queue full", p)
|
||||
rs.tracer.DropRPC(out, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) Join(bitmask []byte) {
|
||||
rs.tracer.Join(bitmask)
|
||||
}
|
||||
|
||||
func (rs *RandomSubRouter) Leave(bitmask []byte) {
|
||||
rs.tracer.Join(bitmask)
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
package blossomsub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
)
|
||||
|
||||
func getRandomsub(ctx context.Context, h host.Host, size int, opts ...Option) *PubSub {
|
||||
ps, err := NewRandomSub(ctx, h, size, opts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
func getRandomsubs(ctx context.Context, hs []host.Host, size int, opts ...Option) []*PubSub {
|
||||
var psubs []*PubSub
|
||||
for _, h := range hs {
|
||||
psubs = append(psubs, getRandomsub(ctx, h, size, opts...))
|
||||
}
|
||||
return psubs
|
||||
}
|
||||
|
||||
func tryReceive(sub *Subscription) *Message {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
m, err := sub.Next(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
} else {
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomsubSmall(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 10)
|
||||
psubs := getRandomsubs(ctx, hosts, 10)
|
||||
|
||||
connectAll(t, hosts)
|
||||
|
||||
var subs []*Subscription
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
count := 0
|
||||
for i := 0; i < 10; i++ {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
|
||||
|
||||
for _, sub := range subs {
|
||||
if tryReceive(sub) != nil {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count < 7*len(hosts) {
|
||||
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomsubBig(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 50)
|
||||
psubs := getRandomsubs(ctx, hosts, 50)
|
||||
|
||||
connectSome(t, hosts, 12)
|
||||
|
||||
var subs []*Subscription
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
count := 0
|
||||
for i := 0; i < 10; i++ {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
|
||||
|
||||
for _, sub := range subs {
|
||||
if tryReceive(sub) != nil {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count < 7*len(hosts) {
|
||||
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomsubMixed(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 40)
|
||||
fsubs := getPubsubs(ctx, hosts[:10])
|
||||
rsubs := getRandomsubs(ctx, hosts[10:], 30)
|
||||
psubs := append(fsubs, rsubs...)
|
||||
|
||||
connectSome(t, hosts, 12)
|
||||
|
||||
var subs []*Subscription
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
count := 0
|
||||
for i := 0; i < 10; i++ {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
|
||||
|
||||
for _, sub := range subs {
|
||||
if tryReceive(sub) != nil {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count < 7*len(hosts) {
|
||||
t.Fatalf("received too few messages; expected at least %d but got %d", 9*len(hosts), count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomsubEnoughPeers(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 40)
|
||||
fsubs := getPubsubs(ctx, hosts[:10])
|
||||
rsubs := getRandomsubs(ctx, hosts[10:], 30)
|
||||
psubs := append(fsubs, rsubs...)
|
||||
|
||||
connectSome(t, hosts, 12)
|
||||
|
||||
for _, ps := range psubs {
|
||||
_, err := ps.Subscribe([]byte{0x7e, 0x57})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
res := make(chan bool, 1)
|
||||
rsubs[0].eval <- func() {
|
||||
rs := rsubs[0].rt.(*RandomSubRouter)
|
||||
res <- rs.EnoughPeers([]byte{0x7e, 0x57}, 0)
|
||||
}
|
||||
|
||||
enough := <-res
|
||||
if !enough {
|
||||
t.Fatal("expected enough peers")
|
||||
}
|
||||
|
||||
rsubs[0].eval <- func() {
|
||||
rs := rsubs[0].rt.(*RandomSubRouter)
|
||||
res <- rs.EnoughPeers([]byte{0x7e, 0x57}, 100)
|
||||
}
|
||||
|
||||
enough = <-res
|
||||
if !enough {
|
||||
t.Fatal("expected enough peers")
|
||||
}
|
||||
}
|
@ -105,7 +105,7 @@ type deliveryRecord struct {
|
||||
}
|
||||
|
||||
type deliveryEntry struct {
|
||||
id string
|
||||
id []byte
|
||||
expire time.Time
|
||||
next *deliveryEntry
|
||||
}
|
||||
@ -200,12 +200,12 @@ func newPeerScore(params *PeerScoreParams) *peerScore {
|
||||
// Note: assumes that the bitmask score parameters have already been validated
|
||||
func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams) error {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
old, exist := ps.params.Bitmasks[string(bitmask)]
|
||||
ps.params.Bitmasks[string(bitmask)] = p
|
||||
|
||||
if !exist {
|
||||
ps.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -218,6 +218,7 @@ func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams
|
||||
recap = true
|
||||
}
|
||||
if !recap {
|
||||
ps.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -236,7 +237,7 @@ func (ps *peerScore) SetBitmaskScoreParams(bitmask []byte, p *BitmaskScoreParams
|
||||
tstats.meshMessageDeliveries = p.MeshMessageDeliveriesCap
|
||||
}
|
||||
}
|
||||
|
||||
ps.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -257,9 +258,10 @@ func (ps *peerScore) Score(p peer.ID) float64 {
|
||||
}
|
||||
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
return ps.score(p)
|
||||
score := ps.score(p)
|
||||
ps.Unlock()
|
||||
return score
|
||||
}
|
||||
|
||||
func (ps *peerScore) score(p peer.ID) float64 {
|
||||
@ -394,14 +396,15 @@ func (ps *peerScore) AddPenalty(p peer.ID, count int) {
|
||||
}
|
||||
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
pstats, ok := ps.peerStats[p]
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
pstats.behaviourPenalty += float64(count)
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
// periodic maintenance
|
||||
@ -503,7 +506,6 @@ func (ps *peerScore) inspectScoresExtended() {
|
||||
// once their expiry has elapsed.
|
||||
func (ps *peerScore) refreshScores() {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for p, pstats := range ps.peerStats {
|
||||
@ -562,12 +564,13 @@ func (ps *peerScore) refreshScores() {
|
||||
pstats.behaviourPenalty = 0
|
||||
}
|
||||
}
|
||||
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
// refreshIPs refreshes IPs we know of peers we're tracking.
|
||||
func (ps *peerScore) refreshIPs() {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
// peer IPs may change, so we periodically refresh them
|
||||
//
|
||||
@ -582,19 +585,20 @@ func (ps *peerScore) refreshIPs() {
|
||||
pstats.ips = ips
|
||||
}
|
||||
}
|
||||
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) gcDeliveryRecords() {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
ps.deliveries.gc()
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
// tracer interface
|
||||
func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
pstats, ok := ps.peerStats[p]
|
||||
if !ok {
|
||||
@ -606,14 +610,15 @@ func (ps *peerScore) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
ips := ps.getIPs(p)
|
||||
ps.setIPs(p, ips, pstats.ips)
|
||||
pstats.ips = ips
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) RemovePeer(p peer.ID) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
pstats, ok := ps.peerStats[p]
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -622,6 +627,7 @@ func (ps *peerScore) RemovePeer(p peer.ID) {
|
||||
if ps.score(p) > 0 {
|
||||
ps.removeIPs(p, pstats.ips)
|
||||
delete(ps.peerStats, p)
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -641,6 +647,7 @@ func (ps *peerScore) RemovePeer(p peer.ID) {
|
||||
|
||||
pstats.connected = false
|
||||
pstats.expire = time.Now().Add(ps.params.RetainScore)
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) Join(bitmask []byte) {}
|
||||
@ -648,15 +655,16 @@ func (ps *peerScore) Leave(bitmask []byte) {}
|
||||
|
||||
func (ps *peerScore) Graft(p peer.ID, bitmask []byte) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
pstats, ok := ps.peerStats[p]
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
tstats, ok := pstats.getBitmaskStats(bitmask, ps.params)
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -664,19 +672,21 @@ func (ps *peerScore) Graft(p peer.ID, bitmask []byte) {
|
||||
tstats.graftTime = time.Now()
|
||||
tstats.meshTime = 0
|
||||
tstats.meshMessageDeliveriesActive = false
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) Prune(p peer.ID, bitmask []byte) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
pstats, ok := ps.peerStats[p]
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
tstats, ok := pstats.getBitmaskStats(bitmask, ps.params)
|
||||
if !ok {
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -688,20 +698,20 @@ func (ps *peerScore) Prune(p peer.ID, bitmask []byte) {
|
||||
}
|
||||
|
||||
tstats.inMesh = false
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) ValidateMessage(msg *Message) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
// the pubsub subsystem is beginning validation; create a record to track time in
|
||||
// the validation pipeline with an accurate firstSeen time.
|
||||
_ = ps.deliveries.getRecord(ps.idGen.ID(msg))
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) DeliverMessage(msg *Message) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
ps.markFirstMessageDelivery(msg.ReceivedFrom, msg)
|
||||
|
||||
@ -710,6 +720,7 @@ func (ps *peerScore) DeliverMessage(msg *Message) {
|
||||
// defensive check that this is the first delivery trace -- delivery status should be unknown
|
||||
if drec.status != deliveryUnknown {
|
||||
log.Debugf("unexpected delivery trace: message from %s was first seen %s ago and has delivery status %d", msg.ReceivedFrom, time.Since(drec.firstSeen), drec.status)
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -723,11 +734,11 @@ func (ps *peerScore) DeliverMessage(msg *Message) {
|
||||
ps.markDuplicateMessageDelivery(p, msg, time.Time{})
|
||||
}
|
||||
}
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) RejectMessage(msg *Message, reason string) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
switch reason {
|
||||
// we don't track those messages, but we penalize the peer as they are clearly invalid
|
||||
@ -741,18 +752,21 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
|
||||
fallthrough
|
||||
case RejectSelfOrigin:
|
||||
ps.markInvalidMessageDelivery(msg.ReceivedFrom, msg)
|
||||
ps.Unlock()
|
||||
return
|
||||
|
||||
// we ignore those messages, so do nothing.
|
||||
case RejectBlacklstedPeer:
|
||||
fallthrough
|
||||
case RejectBlacklistedSource:
|
||||
ps.Unlock()
|
||||
return
|
||||
|
||||
case RejectValidationQueueFull:
|
||||
// the message was rejected before it entered the validation pipeline;
|
||||
// we don't know if this message has a valid signature, and thus we also don't know if
|
||||
// it has a valid message ID; all we can do is ignore it.
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -761,6 +775,7 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
|
||||
// defensive check that this is the first rejection trace -- delivery status should be unknown
|
||||
if drec.status != deliveryUnknown {
|
||||
log.Debugf("unexpected rejection trace: message from %s was first seen %s ago and has delivery status %d", msg.ReceivedFrom, time.Since(drec.firstSeen), drec.status)
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -771,12 +786,14 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
|
||||
drec.status = deliveryThrottled
|
||||
// release the delivery time tracking map to free some memory early
|
||||
drec.peers = nil
|
||||
ps.Unlock()
|
||||
return
|
||||
case RejectValidationIgnored:
|
||||
// we were explicitly instructed by the validator to ignore the message but not penalize
|
||||
// the peer
|
||||
drec.status = deliveryIgnored
|
||||
drec.peers = nil
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -790,17 +807,18 @@ func (ps *peerScore) RejectMessage(msg *Message, reason string) {
|
||||
|
||||
// release the delivery time tracking map to free some memory early
|
||||
drec.peers = nil
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) DuplicateMessage(msg *Message) {
|
||||
ps.Lock()
|
||||
defer ps.Unlock()
|
||||
|
||||
drec := ps.deliveries.getRecord(ps.idGen.ID(msg))
|
||||
|
||||
_, ok := drec.peers[msg.ReceivedFrom]
|
||||
if ok {
|
||||
// we have already seen this duplicate!
|
||||
ps.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -824,6 +842,7 @@ func (ps *peerScore) DuplicateMessage(msg *Message) {
|
||||
case deliveryIgnored:
|
||||
// the message was ignored; do nothing
|
||||
}
|
||||
ps.Unlock()
|
||||
}
|
||||
|
||||
func (ps *peerScore) ThrottlePeer(p peer.ID) {}
|
||||
@ -837,8 +856,8 @@ func (ps *peerScore) DropRPC(rpc *RPC, p peer.ID) {}
|
||||
func (ps *peerScore) UndeliverableMessage(msg *Message) {}
|
||||
|
||||
// message delivery records
|
||||
func (d *messageDeliveries) getRecord(id string) *deliveryRecord {
|
||||
rec, ok := d.records[id]
|
||||
func (d *messageDeliveries) getRecord(id []byte) *deliveryRecord {
|
||||
rec, ok := d.records[string(id)]
|
||||
if ok {
|
||||
return rec
|
||||
}
|
||||
@ -846,7 +865,7 @@ func (d *messageDeliveries) getRecord(id string) *deliveryRecord {
|
||||
now := time.Now()
|
||||
|
||||
rec = &deliveryRecord{peers: make(map[peer.ID]struct{}), firstSeen: now}
|
||||
d.records[id] = rec
|
||||
d.records[string(id)] = rec
|
||||
|
||||
entry := &deliveryEntry{id: id, expire: now.Add(d.seenMsgTTL)}
|
||||
if d.tail != nil {
|
||||
@ -867,7 +886,7 @@ func (d *messageDeliveries) gc() {
|
||||
|
||||
now := time.Now()
|
||||
for d.head != nil && now.After(d.head.expire) {
|
||||
delete(d.records, d.head.id)
|
||||
delete(d.records, string(d.head.id))
|
||||
d.head = d.head.next
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package blossomsub
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
@ -26,10 +27,8 @@ const (
|
||||
// StrictNoSign does not produce signatures and drops and penalises incoming messages that carry one
|
||||
StrictNoSign = msgVerification
|
||||
// LaxSign produces signatures and validates incoming signatures iff one is present
|
||||
// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
|
||||
LaxSign = msgSigning
|
||||
// LaxNoSign does not produce signatures and validates incoming signatures iff one is present
|
||||
// Deprecated: it is recommend to either strictly enable, or strictly disable, signatures.
|
||||
LaxNoSign = 0
|
||||
)
|
||||
|
||||
@ -52,7 +51,7 @@ func verifyMessageSignature(m *pb.Message) error {
|
||||
return err
|
||||
}
|
||||
|
||||
xm := *m
|
||||
xm := (proto.Clone(m)).(*pb.Message)
|
||||
xm.Signature = nil
|
||||
xm.Key = nil
|
||||
bytes, err := xm.Marshal()
|
||||
|
@ -11,12 +11,25 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
)
|
||||
|
||||
func mustSubscribe(t *testing.T, ps *PubSub, bitmask []byte) *Subscription {
|
||||
sub, err := ps.Subscribe(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(sub) != 1 {
|
||||
t.Fatal("must subscribe only allows single bit bitmasks")
|
||||
}
|
||||
|
||||
return sub[0]
|
||||
}
|
||||
|
||||
func TestBasicSubscriptionFilter(t *testing.T) {
|
||||
peerA := peer.ID("A")
|
||||
|
||||
bitmask1 := []byte{0xff, 0x00, 0x00, 0x00}
|
||||
bitmask2 := []byte{0x00, 0xff, 0x00, 0x00}
|
||||
bitmask3 := []byte{0x00, 0x00, 0xff, 0x00}
|
||||
bitmask1 := []byte{0x00, 0x80, 0x00, 0x00}
|
||||
bitmask2 := []byte{0x00, 0x20, 0x00, 0x00}
|
||||
bitmask3 := []byte{0x00, 0x00, 0x02, 0x00}
|
||||
yes := true
|
||||
subs := []*pb.RPC_SubOpts{
|
||||
&pb.RPC_SubOpts{
|
||||
@ -69,9 +82,9 @@ func TestBasicSubscriptionFilter(t *testing.T) {
|
||||
func TestSubscriptionFilterDeduplication(t *testing.T) {
|
||||
peerA := peer.ID("A")
|
||||
|
||||
bitmask1 := []byte{0xff, 0x00, 0x00, 0x00}
|
||||
bitmask2 := []byte{0x00, 0xff, 0x00, 0x00}
|
||||
bitmask3 := []byte{0x00, 0x00, 0xff, 0x00}
|
||||
bitmask1 := []byte{0x00, 0x80, 0x00, 0x00}
|
||||
bitmask2 := []byte{0x00, 0x20, 0x00, 0x00}
|
||||
bitmask3 := []byte{0x00, 0x00, 0x02, 0x00}
|
||||
yes := true
|
||||
no := false
|
||||
subs := []*pb.RPC_SubOpts{
|
||||
@ -117,17 +130,17 @@ func TestSubscriptionFilterRPC(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
ps1 := getPubsub(ctx, hosts[0], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0xff, 0x00, 0x00, 0x00}, []byte{0x00, 0xff, 0x00, 0x00})))
|
||||
ps2 := getPubsub(ctx, hosts[1], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0xff, 0x00, 0x00}, []byte{0x00, 0x00, 0xff, 0x00})))
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
ps1 := getBlossomSub(ctx, hosts[0], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0x80, 0x00, 0x00}, []byte{0x00, 0x20, 0x00, 0x00})))
|
||||
ps2 := getBlossomSub(ctx, hosts[1], WithSubscriptionFilter(NewAllowlistSubscriptionFilter([]byte{0x00, 0x20, 0x00, 0x00}, []byte{0x00, 0x00, 0x02, 0x00})))
|
||||
|
||||
_ = mustSubscribe(t, ps1, []byte{0xff, 0x00, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps1, []byte{0x00, 0xff, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps2, []byte{0x00, 0xff, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps2, []byte{0x00, 0x00, 0xff, 0x00})
|
||||
_ = mustSubscribe(t, ps1, []byte{0x00, 0x80, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps1, []byte{0x00, 0x20, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps2, []byte{0x00, 0x20, 0x00, 0x00})
|
||||
_ = mustSubscribe(t, ps2, []byte{0x00, 0x00, 0x02, 0x00})
|
||||
|
||||
// check the rejection as well
|
||||
_, err := ps1.Join([]byte{0x00, 0x00, 0xff, 0x00})
|
||||
_, err := ps1.Join([]byte{0x00, 0x00, 0x02, 0x00})
|
||||
if err == nil {
|
||||
t.Fatal("expected subscription error")
|
||||
}
|
||||
@ -140,9 +153,9 @@ func TestSubscriptionFilterRPC(t *testing.T) {
|
||||
ready := make(chan struct{})
|
||||
|
||||
ps1.eval <- func() {
|
||||
_, sub1 = ps1.bitmasks[string([]byte{0xff, 0x00, 0x00, 0x00})][hosts[1].ID()]
|
||||
_, sub2 = ps1.bitmasks[string([]byte{0x00, 0xff, 0x00, 0x00})][hosts[1].ID()]
|
||||
_, sub3 = ps1.bitmasks[string([]byte{0x00, 0x00, 0xff, 0x00})][hosts[1].ID()]
|
||||
_, sub1 = ps1.bitmasks[string([]byte{0x00, 0x80, 0x00, 0x00})][hosts[1].ID()]
|
||||
_, sub2 = ps1.bitmasks[string([]byte{0x00, 0x20, 0x00, 0x00})][hosts[1].ID()]
|
||||
_, sub3 = ps1.bitmasks[string([]byte{0x00, 0x00, 0x02, 0x00})][hosts[1].ID()]
|
||||
ready <- struct{}{}
|
||||
}
|
||||
<-ready
|
||||
@ -158,9 +171,9 @@ func TestSubscriptionFilterRPC(t *testing.T) {
|
||||
}
|
||||
|
||||
ps2.eval <- func() {
|
||||
_, sub1 = ps2.bitmasks[string([]byte{0xff, 0x00, 0x00, 0x00})][hosts[0].ID()]
|
||||
_, sub2 = ps2.bitmasks[string([]byte{0x00, 0xff, 0x00, 0x00})][hosts[0].ID()]
|
||||
_, sub3 = ps2.bitmasks[string([]byte{0x00, 0x00, 0xff, 0x00})][hosts[0].ID()]
|
||||
_, sub1 = ps2.bitmasks[string([]byte{0x00, 0x80, 0x00, 0x00})][hosts[0].ID()]
|
||||
_, sub2 = ps2.bitmasks[string([]byte{0x00, 0x20, 0x00, 0x00})][hosts[0].ID()]
|
||||
_, sub3 = ps2.bitmasks[string([]byte{0x00, 0x00, 0x02, 0x00})][hosts[0].ID()]
|
||||
ready <- struct{}{}
|
||||
}
|
||||
<-ready
|
||||
|
@ -109,9 +109,9 @@ func (t *tagTracer) addDeliveryTag(bitmask []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("pubsub-deliveries:%s", bitmask)
|
||||
name := "pubsub-deliveries:" + string(bitmask)
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
tag, err := t.decayer.RegisterDecayingTag(
|
||||
name,
|
||||
BlossomSubConnTagDecayInterval,
|
||||
@ -120,16 +120,19 @@ func (t *tagTracer) addDeliveryTag(bitmask []byte) {
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("unable to create decaying delivery tag: %s", err)
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
t.decaying[string(bitmask)] = tag
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tagTracer) removeDeliveryTag(bitmask []byte) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
tag, ok := t.decaying[string(bitmask)]
|
||||
if !ok {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
err := tag.Close()
|
||||
@ -137,17 +140,20 @@ func (t *tagTracer) removeDeliveryTag(bitmask []byte) {
|
||||
log.Warnf("error closing decaying connmgr tag: %s", err)
|
||||
}
|
||||
delete(t.decaying, string(bitmask))
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tagTracer) bumpDeliveryTag(p peer.ID, bitmask []byte) error {
|
||||
t.RLock()
|
||||
defer t.RUnlock()
|
||||
|
||||
tag, ok := t.decaying[string(bitmask)]
|
||||
if !ok {
|
||||
t.RUnlock()
|
||||
return fmt.Errorf("no decaying tag registered for bitmask %s", bitmask)
|
||||
}
|
||||
return tag.Bump(p, BlossomSubConnTagBumpMessageDelivery)
|
||||
err := tag.Bump(p, BlossomSubConnTagBumpMessageDelivery)
|
||||
t.RUnlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) {
|
||||
@ -161,15 +167,17 @@ func (t *tagTracer) bumpTagsForMessage(p peer.ID, msg *Message) {
|
||||
// nearFirstPeers returns the peers who delivered the message while it was still validating
|
||||
func (t *tagTracer) nearFirstPeers(msg *Message) []peer.ID {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
peersMap, ok := t.nearFirst[t.idGen.ID(msg)]
|
||||
|
||||
peersMap, ok := t.nearFirst[string(t.idGen.ID(msg))]
|
||||
if !ok {
|
||||
t.Unlock()
|
||||
return nil
|
||||
}
|
||||
peers := make([]peer.ID, 0, len(peersMap))
|
||||
for p := range peersMap {
|
||||
peers = append(peers, p)
|
||||
}
|
||||
t.Unlock()
|
||||
return peers
|
||||
}
|
||||
|
||||
@ -194,7 +202,7 @@ func (t *tagTracer) DeliverMessage(msg *Message) {
|
||||
|
||||
// delete the delivery state for this message
|
||||
t.Lock()
|
||||
delete(t.nearFirst, t.idGen.ID(msg))
|
||||
delete(t.nearFirst, string(t.idGen.ID(msg)))
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
@ -212,31 +220,32 @@ func (t *tagTracer) Prune(p peer.ID, bitmask []byte) {
|
||||
|
||||
func (t *tagTracer) ValidateMessage(msg *Message) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
// create map to start tracking the peers who deliver while we're validating
|
||||
id := t.idGen.ID(msg)
|
||||
if _, exists := t.nearFirst[id]; exists {
|
||||
if _, exists := t.nearFirst[string(id)]; exists {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
t.nearFirst[id] = make(map[peer.ID]struct{})
|
||||
t.nearFirst[string(id)] = make(map[peer.ID]struct{})
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tagTracer) DuplicateMessage(msg *Message) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
id := t.idGen.ID(msg)
|
||||
peers, ok := t.nearFirst[id]
|
||||
peers, ok := t.nearFirst[string(id)]
|
||||
if !ok {
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
peers[msg.ReceivedFrom] = struct{}{}
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tagTracer) RejectMessage(msg *Message, reason string) {
|
||||
t.Lock()
|
||||
defer t.Unlock()
|
||||
|
||||
// We want to delete the near-first delivery tracking for messages that have passed through
|
||||
// the validation pipeline. Other rejection reasons (missing signature, etc) skip the validation
|
||||
@ -247,8 +256,9 @@ func (t *tagTracer) RejectMessage(msg *Message, reason string) {
|
||||
case RejectValidationIgnored:
|
||||
fallthrough
|
||||
case RejectValidationFailed:
|
||||
delete(t.nearFirst, t.idGen.ID(msg))
|
||||
delete(t.nearFirst, string(t.idGen.ID(msg)))
|
||||
}
|
||||
t.Unlock()
|
||||
}
|
||||
|
||||
func (t *tagTracer) RemovePeer(peer.ID) {}
|
||||
|
@ -55,9 +55,9 @@ func TestTagTracerDirectPeerTags(t *testing.T) {
|
||||
tt.direct = make(map[peer.ID]struct{})
|
||||
tt.direct[p1] = struct{}{}
|
||||
|
||||
tt.AddPeer(p1, BlossomSubID_v11)
|
||||
tt.AddPeer(p2, BlossomSubID_v11)
|
||||
tt.AddPeer(p3, BlossomSubID_v11)
|
||||
tt.AddPeer(p1, BlossomSubID_v2)
|
||||
tt.AddPeer(p2, BlossomSubID_v2)
|
||||
tt.AddPeer(p3, BlossomSubID_v2)
|
||||
|
||||
tag := "pubsub:<direct>"
|
||||
if !cmgr.IsProtected(p1, tag) {
|
||||
@ -179,7 +179,7 @@ func TestTagTracerDeliveryTagsNearFirst(t *testing.T) {
|
||||
|
||||
tt := newTagTracer(cmgr)
|
||||
|
||||
bitmask := []byte{0x7e, 0x57}
|
||||
bitmask := []byte{0x01, 0x00}
|
||||
|
||||
p := peer.ID("a-peer")
|
||||
p2 := peer.ID("another-peer")
|
||||
|
@ -36,21 +36,22 @@ func (tc *FirstSeenCache) Done() {
|
||||
|
||||
func (tc *FirstSeenCache) Has(s string) bool {
|
||||
tc.lk.RLock()
|
||||
defer tc.lk.RUnlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
tc.lk.RUnlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
func (tc *FirstSeenCache) Add(s string) bool {
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
if ok {
|
||||
tc.lk.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
tc.lk.Unlock()
|
||||
return true
|
||||
}
|
||||
|
@ -37,22 +37,20 @@ func (tc *LastSeenCache) Done() {
|
||||
|
||||
func (tc *LastSeenCache) Add(s string) bool {
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
|
||||
tc.lk.Unlock()
|
||||
return !ok
|
||||
}
|
||||
|
||||
func (tc *LastSeenCache) Has(s string) bool {
|
||||
tc.lk.Lock()
|
||||
defer tc.lk.Unlock()
|
||||
|
||||
_, ok := tc.m[s]
|
||||
if ok {
|
||||
tc.m[s] = time.Now().Add(tc.ttl)
|
||||
}
|
||||
|
||||
tc.lk.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ var backgroundSweepInterval = time.Minute
|
||||
|
||||
func background(ctx context.Context, lk sync.Locker, m map[string]time.Time) {
|
||||
ticker := time.NewTicker(backgroundSweepInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
@ -18,6 +17,7 @@ func background(ctx context.Context, lk sync.Locker, m map[string]time.Time) {
|
||||
sweep(lk, m, now)
|
||||
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -25,11 +25,12 @@ func background(ctx context.Context, lk sync.Locker, m map[string]time.Time) {
|
||||
|
||||
func sweep(lk sync.Locker, m map[string]time.Time, now time.Time) {
|
||||
lk.Lock()
|
||||
defer lk.Unlock()
|
||||
|
||||
for k, expiry := range m {
|
||||
if expiry.Before(now) {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
|
||||
lk.Unlock()
|
||||
}
|
||||
|
@ -148,19 +148,19 @@ func (t *pubsubTracer) DuplicateMessage(msg *Message) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
evt := &pb.TraceEvent{
|
||||
Type: pb.TraceEvent_DUPLICATE_MESSAGE.Enum(),
|
||||
PeerID: []byte(t.pid),
|
||||
Timestamp: &now,
|
||||
DuplicateMessage: &pb.TraceEvent_DuplicateMessage{
|
||||
MessageID: []byte(t.idGen.ID(msg)),
|
||||
ReceivedFrom: []byte(msg.ReceivedFrom),
|
||||
Bitmask: msg.Bitmask,
|
||||
},
|
||||
}
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_DUPLICATE_MESSAGE.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// DuplicateMessage: &pb.TraceEvent_DuplicateMessage{
|
||||
// MessageID: []byte(t.idGen.ID(msg)),
|
||||
// ReceivedFrom: []byte(msg.ReceivedFrom),
|
||||
// Bitmask: msg.Bitmask,
|
||||
// },
|
||||
// }
|
||||
|
||||
t.tracer.Trace(evt)
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) DeliverMessage(msg *Message) {
|
||||
@ -178,19 +178,19 @@ func (t *pubsubTracer) DeliverMessage(msg *Message) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
evt := &pb.TraceEvent{
|
||||
Type: pb.TraceEvent_DELIVER_MESSAGE.Enum(),
|
||||
PeerID: []byte(t.pid),
|
||||
Timestamp: &now,
|
||||
DeliverMessage: &pb.TraceEvent_DeliverMessage{
|
||||
MessageID: []byte(t.idGen.ID(msg)),
|
||||
Bitmask: msg.Bitmask,
|
||||
ReceivedFrom: []byte(msg.ReceivedFrom),
|
||||
},
|
||||
}
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_DELIVER_MESSAGE.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// DeliverMessage: &pb.TraceEvent_DeliverMessage{
|
||||
// MessageID: []byte(t.idGen.ID(msg)),
|
||||
// Bitmask: msg.Bitmask,
|
||||
// ReceivedFrom: []byte(msg.ReceivedFrom),
|
||||
// },
|
||||
// }
|
||||
|
||||
t.tracer.Trace(evt)
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) AddPeer(p peer.ID, proto protocol.ID) {
|
||||
@ -260,18 +260,18 @@ func (t *pubsubTracer) RecvRPC(rpc *RPC) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
evt := &pb.TraceEvent{
|
||||
Type: pb.TraceEvent_RECV_RPC.Enum(),
|
||||
PeerID: []byte(t.pid),
|
||||
Timestamp: &now,
|
||||
RecvRPC: &pb.TraceEvent_RecvRPC{
|
||||
ReceivedFrom: []byte(rpc.from),
|
||||
Meta: t.traceRPCMeta(rpc),
|
||||
},
|
||||
}
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_RECV_RPC.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// RecvRPC: &pb.TraceEvent_RecvRPC{
|
||||
// ReceivedFrom: []byte(rpc.from),
|
||||
// Meta: t.traceRPCMeta(rpc),
|
||||
// },
|
||||
// }
|
||||
|
||||
t.tracer.Trace(evt)
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) SendRPC(rpc *RPC, p peer.ID) {
|
||||
@ -287,18 +287,18 @@ func (t *pubsubTracer) SendRPC(rpc *RPC, p peer.ID) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
evt := &pb.TraceEvent{
|
||||
Type: pb.TraceEvent_SEND_RPC.Enum(),
|
||||
PeerID: []byte(t.pid),
|
||||
Timestamp: &now,
|
||||
SendRPC: &pb.TraceEvent_SendRPC{
|
||||
SendTo: []byte(p),
|
||||
Meta: t.traceRPCMeta(rpc),
|
||||
},
|
||||
}
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_SEND_RPC.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// SendRPC: &pb.TraceEvent_SendRPC{
|
||||
// SendTo: []byte(p),
|
||||
// Meta: t.traceRPCMeta(rpc),
|
||||
// },
|
||||
// }
|
||||
|
||||
t.tracer.Trace(evt)
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) DropRPC(rpc *RPC, p peer.ID) {
|
||||
@ -314,18 +314,18 @@ func (t *pubsubTracer) DropRPC(rpc *RPC, p peer.ID) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UnixNano()
|
||||
evt := &pb.TraceEvent{
|
||||
Type: pb.TraceEvent_DROP_RPC.Enum(),
|
||||
PeerID: []byte(t.pid),
|
||||
Timestamp: &now,
|
||||
DropRPC: &pb.TraceEvent_DropRPC{
|
||||
SendTo: []byte(p),
|
||||
Meta: t.traceRPCMeta(rpc),
|
||||
},
|
||||
}
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_DROP_RPC.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// DropRPC: &pb.TraceEvent_DropRPC{
|
||||
// SendTo: []byte(p),
|
||||
// Meta: t.traceRPCMeta(rpc),
|
||||
// },
|
||||
// }
|
||||
|
||||
t.tracer.Trace(evt)
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) UndeliverableMessage(msg *Message) {
|
||||
@ -336,6 +336,24 @@ func (t *pubsubTracer) UndeliverableMessage(msg *Message) {
|
||||
for _, tr := range t.raw {
|
||||
tr.UndeliverableMessage(msg)
|
||||
}
|
||||
|
||||
if t.tracer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// now := time.Now().UnixNano()
|
||||
// evt := &pb.TraceEvent{
|
||||
// Type: pb.TraceEvent_UNDELIVERABLE_MESSAGE.Enum(),
|
||||
// PeerID: []byte(t.pid),
|
||||
// Timestamp: &now,
|
||||
// UndeliverableMessage: &pb.TraceEvent_UndeliverableMessage{
|
||||
// MessageID: []byte(t.idGen.ID(msg)),
|
||||
// Bitmask: msg.Bitmask,
|
||||
// ReceivedFrom: []byte(msg.ReceivedFrom),
|
||||
// },
|
||||
// }
|
||||
|
||||
// t.tracer.Trace(evt)
|
||||
}
|
||||
|
||||
func (t *pubsubTracer) traceRPCMeta(rpc *RPC) *pb.TraceEvent_RPCMeta {
|
||||
|
@ -11,24 +11,22 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/blank"
|
||||
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
|
||||
|
||||
"github.com/libp2p/go-msgio/protoio"
|
||||
"github.com/libp2p/go-msgio"
|
||||
)
|
||||
|
||||
func testWithTracer(t *testing.T, tracer EventTracer) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 20)
|
||||
hosts := getDefaultHosts(t, 20)
|
||||
psubs := getBlossomSubs(ctx, hosts,
|
||||
WithMessageIdFn(func(pmsg *pb.Message) []byte { return pmsg.Data }),
|
||||
WithEventTracer(tracer),
|
||||
// to bootstrap from star topology
|
||||
WithPeerExchange(true),
|
||||
@ -49,7 +47,7 @@ func testWithTracer(t *testing.T, tracer EventTracer) {
|
||||
|
||||
// add a validator that rejects some messages to exercise those code paths in the tracer
|
||||
for _, ps := range psubs {
|
||||
ps.RegisterBitmaskValidator([]byte{0x7e, 57}, func(ctx context.Context, p peer.ID, msg *Message) bool {
|
||||
ps.RegisterBitmaskValidator([]byte{0x01, 0x00}, func(ctx context.Context, p peer.ID, msg *Message) bool {
|
||||
if string(msg.Data) == "invalid!" {
|
||||
return false
|
||||
} else {
|
||||
@ -79,8 +77,14 @@ func testWithTracer(t *testing.T, tracer EventTracer) {
|
||||
|
||||
// build the mesh
|
||||
var subs []*Subscription
|
||||
var bitmasks []*Bitmask
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0x7e, 0x57})
|
||||
b, err := ps.Join([]byte{0x01, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bitmasks = append(bitmasks, b...)
|
||||
sub, err := ps.Subscribe([]byte{0x01, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -91,8 +95,8 @@ func testWithTracer(t *testing.T, tracer EventTracer) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}(sub)
|
||||
subs = append(subs, sub)
|
||||
}(sub[0])
|
||||
subs = append(subs, sub...)
|
||||
}
|
||||
|
||||
// wait for the mesh to build
|
||||
@ -101,10 +105,14 @@ func testWithTracer(t *testing.T, tracer EventTracer) {
|
||||
// publish some messages
|
||||
for i := 0; i < 20; i++ {
|
||||
if i%7 == 0 {
|
||||
psubs[i].Publish([]byte{0x7e, 0x57}, []byte("invalid!"))
|
||||
bitmasks[i].Publish(ctx, bitmasks[i].bitmask, []byte("invalid!"))
|
||||
} else {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
psubs[i].Publish([]byte{0x7e, 0x57}, msg)
|
||||
if i%9 == 0 {
|
||||
bitmasks[i].Publish(ctx, bitmasks[i].bitmask, []byte("dupe"))
|
||||
} else {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
bitmasks[i].Publish(ctx, bitmasks[i].bitmask, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +133,6 @@ type traceStats struct {
|
||||
}
|
||||
|
||||
func (t *traceStats) process(evt *pb.TraceEvent) {
|
||||
// fmt.Printf("process event %s\n", evt.GetType())
|
||||
switch evt.GetType() {
|
||||
case pb.TraceEvent_PUBLISH_MESSAGE:
|
||||
t.publish++
|
||||
@ -244,10 +251,16 @@ func TestPBTracer(t *testing.T) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := protoio.NewDelimitedReader(f, 1<<20)
|
||||
r := msgio.NewVarintReaderSize(f, DefaultMaxMessageSize)
|
||||
|
||||
for {
|
||||
evt.Reset()
|
||||
err := r.ReadMsg(&evt)
|
||||
v, err := r.ReadMsg()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
err = proto.Unmarshal(v, &evt)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@ -271,12 +284,13 @@ func (mrt *mockRemoteTracer) handleStream(s network.Stream) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
r := protoio.NewDelimitedReader(gzr, 1<<24)
|
||||
r := msgio.NewVarintReader(gzr)
|
||||
|
||||
var batch pb.TraceEventBatch
|
||||
for {
|
||||
batch.Reset()
|
||||
err := r.ReadMsg(&batch)
|
||||
|
||||
v, err := r.ReadMsg()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
s.Reset()
|
||||
@ -284,6 +298,11 @@ func (mrt *mockRemoteTracer) handleStream(s network.Stream) {
|
||||
return
|
||||
}
|
||||
|
||||
err = proto.Unmarshal(v, &batch)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
mrt.mx.Lock()
|
||||
for _, evt := range batch.GetBatch() {
|
||||
mrt.ts.process(evt)
|
||||
@ -302,10 +321,9 @@ func TestRemoteTracer(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
h1 := bhost.NewBlankHost(swarmt.GenSwarm(t))
|
||||
h2 := bhost.NewBlankHost(swarmt.GenSwarm(t))
|
||||
defer h1.Close()
|
||||
defer h2.Close()
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
h1 := hosts[0]
|
||||
h2 := hosts[1]
|
||||
|
||||
mrt := &mockRemoteTracer{}
|
||||
h1.SetStreamHandler(RemoteTracerProtoID, mrt.handleStream)
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
@ -16,8 +17,7 @@ import (
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
|
||||
"github.com/libp2p/go-msgio/protoio"
|
||||
"github.com/libp2p/go-msgio"
|
||||
)
|
||||
|
||||
var TraceBufferSize = 1 << 16 // 64K ought to be enough for everyone; famous last words.
|
||||
@ -48,9 +48,9 @@ type basicTracer struct {
|
||||
|
||||
func (t *basicTracer) Trace(evt *pb.TraceEvent) {
|
||||
t.mx.Lock()
|
||||
defer t.mx.Unlock()
|
||||
|
||||
if t.closed {
|
||||
t.mx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ func (t *basicTracer) Trace(evt *pb.TraceEvent) {
|
||||
} else {
|
||||
t.buf = append(t.buf, evt)
|
||||
}
|
||||
t.mx.Unlock()
|
||||
|
||||
select {
|
||||
case t.ch <- struct{}{}:
|
||||
@ -68,11 +69,11 @@ func (t *basicTracer) Trace(evt *pb.TraceEvent) {
|
||||
|
||||
func (t *basicTracer) Close() {
|
||||
t.mx.Lock()
|
||||
defer t.mx.Unlock()
|
||||
if !t.closed {
|
||||
t.closed = true
|
||||
close(t.ch)
|
||||
}
|
||||
t.mx.Unlock()
|
||||
}
|
||||
|
||||
// JSONTracer is a tracer that writes events to a file, encoded in ndjson.
|
||||
@ -160,7 +161,6 @@ func OpenPBTracer(file string, flags int, perm os.FileMode) (*PBTracer, error) {
|
||||
|
||||
func (t *PBTracer) doWrite() {
|
||||
var buf []*pb.TraceEvent
|
||||
w := protoio.NewDelimitedWriter(t.w)
|
||||
for {
|
||||
_, ok := <-t.ch
|
||||
|
||||
@ -170,11 +170,20 @@ func (t *PBTracer) doWrite() {
|
||||
buf = tmp
|
||||
t.mx.Unlock()
|
||||
|
||||
w := msgio.NewVarintWriter(t.w)
|
||||
|
||||
for i, evt := range buf {
|
||||
err := w.WriteMsg(evt)
|
||||
out, err := proto.Marshal(evt)
|
||||
if err != nil {
|
||||
log.Warnf("error writing event trace: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
err = w.WriteMsg(out)
|
||||
if err != nil {
|
||||
log.Warnf("error writing event trace: %s", err.Error())
|
||||
}
|
||||
|
||||
buf[i] = nil
|
||||
}
|
||||
|
||||
@ -187,7 +196,7 @@ func (t *PBTracer) doWrite() {
|
||||
|
||||
var _ EventTracer = (*PBTracer)(nil)
|
||||
|
||||
const RemoteTracerProtoID = protocol.ID("/libp2p/pubsub/tracer/1.0.0")
|
||||
const RemoteTracerProtoID = protocol.ID("/libp2p/pubsub/tracer/2.0.0")
|
||||
|
||||
// RemoteTracer is a tracer that sends trace events to a remote peer
|
||||
type RemoteTracer struct {
|
||||
@ -217,7 +226,7 @@ func (t *RemoteTracer) doWrite() {
|
||||
var batch pb.TraceEventBatch
|
||||
|
||||
gzipW := gzip.NewWriter(s)
|
||||
w := protoio.NewDelimitedWriter(gzipW)
|
||||
w := msgio.NewVarintWriter(gzipW)
|
||||
|
||||
for {
|
||||
_, ok := <-t.ch
|
||||
@ -235,6 +244,7 @@ func (t *RemoteTracer) doWrite() {
|
||||
tmp := t.buf
|
||||
t.buf = buf[:0]
|
||||
buf = tmp
|
||||
var out []byte
|
||||
t.mx.Unlock()
|
||||
|
||||
if len(buf) == 0 {
|
||||
@ -242,8 +252,13 @@ func (t *RemoteTracer) doWrite() {
|
||||
}
|
||||
|
||||
batch.Batch = buf
|
||||
out, err = proto.Marshal(&batch)
|
||||
if err != nil {
|
||||
log.Debugf("error marshaling trace event batch: %s", err)
|
||||
goto end
|
||||
}
|
||||
|
||||
err = w.WriteMsg(&batch)
|
||||
err = w.WriteMsg(out)
|
||||
if err != nil {
|
||||
log.Debugf("error writing trace event batch: %s", err)
|
||||
goto end
|
||||
@ -251,7 +266,7 @@ func (t *RemoteTracer) doWrite() {
|
||||
|
||||
err = gzipW.Flush()
|
||||
if err != nil {
|
||||
log.Debugf("error flushin gzip stream: %s", err)
|
||||
log.Debugf("error flushing gzip stream: %s", err)
|
||||
goto end
|
||||
}
|
||||
|
||||
|
@ -146,17 +146,18 @@ func (v *validation) AddValidator(req *addValReq) {
|
||||
}
|
||||
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
bitmask := val.bitmask
|
||||
|
||||
_, ok := v.bitmaskVals[string(bitmask)]
|
||||
if ok {
|
||||
v.mx.Unlock()
|
||||
req.resp <- fmt.Errorf("duplicate validator for bitmask %s", bitmask)
|
||||
return
|
||||
}
|
||||
|
||||
v.bitmaskVals[string(bitmask)] = val
|
||||
v.mx.Unlock()
|
||||
req.resp <- nil
|
||||
}
|
||||
|
||||
@ -213,15 +214,16 @@ func (v *validation) makeValidator(req *addValReq) (*validatorImpl, error) {
|
||||
// RemoveValidator removes an existing validator
|
||||
func (v *validation) RemoveValidator(req *rmValReq) {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
bitmask := req.bitmask
|
||||
|
||||
_, ok := v.bitmaskVals[string(bitmask)]
|
||||
if ok {
|
||||
delete(v.bitmaskVals, string(bitmask))
|
||||
v.mx.Unlock()
|
||||
req.resp <- nil
|
||||
} else {
|
||||
v.mx.Unlock()
|
||||
req.resp <- fmt.Errorf("no validator for bitmask %s", bitmask)
|
||||
}
|
||||
}
|
||||
@ -262,7 +264,6 @@ func (v *validation) Push(src peer.ID, msg *Message) bool {
|
||||
// getValidators returns all validators that apply to a given message
|
||||
func (v *validation) getValidators(msg *Message) []*validatorImpl {
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
var vals []*validatorImpl
|
||||
vals = append(vals, v.defaultVals...)
|
||||
@ -271,10 +272,13 @@ func (v *validation) getValidators(msg *Message) []*validatorImpl {
|
||||
|
||||
val, ok := v.bitmaskVals[string(bitmask)]
|
||||
if !ok {
|
||||
v.mx.Unlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
return append(vals, val)
|
||||
impls := append(vals, val)
|
||||
v.mx.Unlock()
|
||||
return impls
|
||||
}
|
||||
|
||||
// validateWorker is an active goroutine performing inline validation
|
||||
@ -293,7 +297,7 @@ func (v *validation) validateWorker() {
|
||||
func (v *validation) validate(vals []*validatorImpl, src peer.ID, msg *Message, synchronous bool) error {
|
||||
// If signature verification is enabled, but signing is disabled,
|
||||
// the Signature is required to be nil upon receiving the message in PubSub.pushMsg.
|
||||
if msg.Signature != nil {
|
||||
if msg.Signature != nil && v.p.signPolicy&msgVerification != 0 {
|
||||
if !v.validateSignature(msg) {
|
||||
log.Debugf("message signature validation failed; dropping message from %s", src)
|
||||
v.tracer.RejectMessage(msg, RejectInvalidSignature)
|
||||
@ -413,7 +417,6 @@ func (v *validation) validateBitmask(vals []*validatorImpl, src peer.ID, msg *Me
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(v.p.ctx)
|
||||
defer cancel()
|
||||
|
||||
rch := make(chan ValidationResult, len(vals))
|
||||
rcount := 0
|
||||
@ -433,6 +436,7 @@ func (v *validation) validateBitmask(vals []*validatorImpl, src peer.ID, msg *Me
|
||||
rch <- validationThrottled
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
|
||||
result := ValidationAccept
|
||||
loop:
|
||||
@ -472,14 +476,10 @@ func (v *validation) validateSingleBitmask(val *validatorImpl, src peer.ID, msg
|
||||
|
||||
func (val *validatorImpl) validateMsg(ctx context.Context, src peer.ID, msg *Message) ValidationResult {
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
log.Debugf("validation done; took %s", time.Since(start))
|
||||
}()
|
||||
var cancel func() = nil
|
||||
|
||||
if val.validateTimeout > 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, val.validateTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
r := val.validate(ctx, src, msg)
|
||||
@ -489,10 +489,18 @@ func (val *validatorImpl) validateMsg(ctx context.Context, src peer.ID, msg *Mes
|
||||
case ValidationReject:
|
||||
fallthrough
|
||||
case ValidationIgnore:
|
||||
log.Debugf("validation done; took %s", time.Since(start))
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
return r
|
||||
|
||||
default:
|
||||
log.Warnf("Unexpected result from validator: %d; ignoring message", r)
|
||||
log.Debugf("validation done; took %s", time.Since(start))
|
||||
if cancel != nil {
|
||||
cancel()
|
||||
}
|
||||
return ValidationIgnore
|
||||
}
|
||||
}
|
||||
|
@ -72,11 +72,11 @@ func (v *BasicSeqnoValidator) validate(ctx context.Context, _ peer.ID, m *Messag
|
||||
|
||||
// get the nonce and compare again with an exclusive lock before commiting (cf concurrent validation)
|
||||
v.mx.Lock()
|
||||
defer v.mx.Unlock()
|
||||
|
||||
nonceBytes, err = v.meta.Get(ctx, p)
|
||||
if err != nil {
|
||||
log.Warn("error retrieving peer nonce: %s", err)
|
||||
v.mx.Unlock()
|
||||
return ValidationIgnore
|
||||
}
|
||||
|
||||
@ -85,6 +85,7 @@ func (v *BasicSeqnoValidator) validate(ctx context.Context, _ peer.ID, m *Messag
|
||||
}
|
||||
|
||||
if seqno <= nonce {
|
||||
v.mx.Unlock()
|
||||
return ValidationIgnore
|
||||
}
|
||||
|
||||
@ -96,6 +97,6 @@ func (v *BasicSeqnoValidator) validate(ctx context.Context, _ peer.ID, m *Messag
|
||||
if err != nil {
|
||||
log.Warn("error storing peer nonce: %s", err)
|
||||
}
|
||||
|
||||
v.mx.Unlock()
|
||||
return ValidationAccept
|
||||
}
|
||||
|
@ -20,6 +20,18 @@ import (
|
||||
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||||
)
|
||||
|
||||
func getBlossomSubsWithOptionC(ctx context.Context, hs []host.Host, cons ...func(int) Option) []*PubSub {
|
||||
var psubs []*PubSub
|
||||
for _, h := range hs {
|
||||
var opts []Option
|
||||
for i, c := range cons {
|
||||
opts = append(opts, c(i))
|
||||
}
|
||||
psubs = append(psubs, getBlossomSub(ctx, h, opts...))
|
||||
}
|
||||
return psubs
|
||||
}
|
||||
|
||||
var rng *rand.Rand
|
||||
|
||||
func init() {
|
||||
@ -38,8 +50,8 @@ func testBasicSeqnoValidator(t *testing.T, ttl time.Duration) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 20)
|
||||
psubs := getPubsubsWithOptionC(ctx, hosts,
|
||||
hosts := getDefaultHosts(t, 20)
|
||||
psubs := getBlossomSubsWithOptionC(ctx, hosts,
|
||||
func(i int) Option {
|
||||
return WithDefaultValidator(NewBasicSeqnoValidator(newMockPeerMetadataStore()))
|
||||
},
|
||||
@ -49,13 +61,19 @@ func testBasicSeqnoValidator(t *testing.T, ttl time.Duration) {
|
||||
)
|
||||
|
||||
var msgs []*Subscription
|
||||
var bitmasks []*Bitmask
|
||||
for _, ps := range psubs {
|
||||
subch, err := ps.Subscribe([]byte{0xf0, 0x0b, 0xa1, 0x20})
|
||||
b, err := ps.Join([]byte{0x00, 0x01})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bitmasks = append(bitmasks, b...)
|
||||
subch, err := ps.Subscribe([]byte{0x00, 0x01})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msgs = append(msgs, subch)
|
||||
msgs = append(msgs, subch...)
|
||||
}
|
||||
|
||||
// connectAll(t, hosts)
|
||||
@ -68,7 +86,7 @@ func testBasicSeqnoValidator(t *testing.T, ttl time.Duration) {
|
||||
|
||||
owner := rng.Intn(len(psubs))
|
||||
|
||||
psubs[owner].Publish([]byte{0xf0, 0x0b, 0xa1, 0x20}, msg)
|
||||
bitmasks[owner].Publish(ctx, bitmasks[owner].bitmask, msg)
|
||||
|
||||
for _, sub := range msgs {
|
||||
got, err := sub.Next(ctx)
|
||||
@ -86,8 +104,8 @@ func TestBasicSeqnoValidatorReplay(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 20)
|
||||
psubs := getPubsubsWithOptionC(ctx, hosts[:19],
|
||||
hosts := getDefaultHosts(t, 20)
|
||||
psubs := getBlossomSubsWithOptionC(ctx, hosts[:19],
|
||||
func(i int) Option {
|
||||
return WithDefaultValidator(NewBasicSeqnoValidator(newMockPeerMetadataStore()))
|
||||
},
|
||||
@ -98,13 +116,19 @@ func TestBasicSeqnoValidatorReplay(t *testing.T) {
|
||||
_ = newReplayActor(t, ctx, hosts[19])
|
||||
|
||||
var msgs []*Subscription
|
||||
var bitmasks []*Bitmask
|
||||
for _, ps := range psubs {
|
||||
subch, err := ps.Subscribe([]byte{0xf0, 0x0b, 0xa1, 0x20})
|
||||
b, err := ps.Join([]byte{0x00, 0x01})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bitmasks = append(bitmasks, b...)
|
||||
subch, err := ps.Subscribe([]byte{0x00, 0x01})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msgs = append(msgs, subch)
|
||||
msgs = append(msgs, subch...)
|
||||
}
|
||||
|
||||
sparseConnect(t, hosts)
|
||||
@ -116,7 +140,7 @@ func TestBasicSeqnoValidatorReplay(t *testing.T) {
|
||||
|
||||
owner := rng.Intn(len(psubs))
|
||||
|
||||
psubs[owner].Publish([]byte{0xf0, 0x0b, 0xa1, 0x20}, msg)
|
||||
bitmasks[owner].Publish(ctx, bitmasks[owner].bitmask, msg)
|
||||
|
||||
for _, sub := range msgs {
|
||||
got, err := sub.Next(ctx)
|
||||
@ -169,7 +193,7 @@ type replayActor struct {
|
||||
|
||||
func newReplayActor(t *testing.T, ctx context.Context, h host.Host) *replayActor {
|
||||
replay := &replayActor{t: t, ctx: ctx, h: h, out: make(map[peer.ID]network.Stream)}
|
||||
h.SetStreamHandler(FloodSubID, replay.handleStream)
|
||||
h.SetStreamHandler(BlossomSubID_v2, replay.handleStream)
|
||||
h.Network().Notify(&network.NotifyBundle{ConnectedF: replay.connected})
|
||||
return replay
|
||||
}
|
||||
@ -246,7 +270,7 @@ func (r *replayActor) replay(msg *pb.Message) {
|
||||
|
||||
var peers []peer.ID
|
||||
r.mx.Lock()
|
||||
for p, _ := range r.out {
|
||||
for p := range r.out {
|
||||
if rng.Intn(2) > 0 {
|
||||
peers = append(peers, p)
|
||||
}
|
||||
@ -262,7 +286,7 @@ func (r *replayActor) replay(msg *pb.Message) {
|
||||
}
|
||||
|
||||
func (r *replayActor) handleConnected(p peer.ID) {
|
||||
s, err := r.h.NewStream(r.ctx, p, FloodSubID)
|
||||
s, err := r.h.NewStream(r.ctx, p, BlossomSubID_v2)
|
||||
if err != nil {
|
||||
r.t.Logf("replay: error opening stream: %s", err)
|
||||
return
|
||||
|
@ -15,8 +15,8 @@ func TestRegisterUnregisterValidator(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 1)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 1)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
err := psubs[0].RegisterBitmaskValidator([]byte{0xf0, 0x00}, func(context.Context, peer.ID, *Message) bool {
|
||||
return true
|
||||
@ -40,10 +40,10 @@ func TestRegisterValidatorEx(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 3)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 3)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
err := psubs[0].RegisterBitmaskValidator([]byte{0x7e, 0x57},
|
||||
err := psubs[0].RegisterBitmaskValidator([]byte{0x01, 0x00},
|
||||
Validator(func(context.Context, peer.ID, *Message) bool {
|
||||
return true
|
||||
}))
|
||||
@ -51,7 +51,7 @@ func TestRegisterValidatorEx(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = psubs[1].RegisterBitmaskValidator([]byte{0x7e, 0x57},
|
||||
err = psubs[1].RegisterBitmaskValidator([]byte{0x01, 0x00},
|
||||
ValidatorEx(func(context.Context, peer.ID, *Message) ValidationResult {
|
||||
return ValidationAccept
|
||||
}))
|
||||
@ -59,7 +59,7 @@ func TestRegisterValidatorEx(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = psubs[2].RegisterBitmaskValidator([]byte{0x7e, 0x57}, "bogus")
|
||||
err = psubs[2].RegisterBitmaskValidator([]byte{0x01, 0x00}, "bogus")
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
@ -69,11 +69,11 @@ func TestValidate(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
connect(t, hosts[0], hosts[1])
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
err := psubs[1].RegisterBitmaskValidator(bitmask, func(ctx context.Context, from peer.ID, msg *Message) bool {
|
||||
return !bytes.Contains(msg.Data, []byte("illegal"))
|
||||
@ -82,6 +82,11 @@ func TestValidate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b, err := psubs[0].Join(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := psubs[1].Subscribe(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -100,13 +105,13 @@ func TestValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range msgs {
|
||||
err := psubs[0].Publish(bitmask, tc.msg)
|
||||
err := b[0].Publish(ctx, b[0].bitmask, tc.msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case msg := <-sub.ch:
|
||||
case msg := <-sub[0].ch:
|
||||
if !tc.validates {
|
||||
t.Log(msg)
|
||||
t.Error("expected message validation to filter out the message")
|
||||
@ -123,10 +128,10 @@ func TestValidate2(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 1)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 1)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
err := psubs[0].RegisterBitmaskValidator(bitmask, func(ctx context.Context, from peer.ID, msg *Message) bool {
|
||||
return !bytes.Contains(msg.Data, []byte("illegal"))
|
||||
@ -145,8 +150,13 @@ func TestValidate2(t *testing.T) {
|
||||
{msg: []byte("but subversive actors will use leetspeek to spread 1ll3g4l content"), validates: true},
|
||||
}
|
||||
|
||||
b, err := psubs[0].Join(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tc := range msgs {
|
||||
err := psubs[0].Publish(bitmask, tc.msg)
|
||||
err := b[0].Publish(ctx, b[0].bitmask, tc.msg)
|
||||
if tc.validates {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -183,7 +193,7 @@ func TestValidateOverload(t *testing.T) {
|
||||
{msg: []byte("still, all good"), validates: true},
|
||||
{msg: []byte("this is getting boring"), validates: true},
|
||||
{msg: []byte([]byte{0xf0, 0x00}), validates: true},
|
||||
{msg: []byte([]byte{0xf0, 0x0b, 0xa1, 0x20}), validates: true},
|
||||
{msg: []byte([]byte{0x00, 0x01}), validates: true},
|
||||
{msg: []byte("foofoo"), validates: true},
|
||||
{msg: []byte("barfoo"), validates: true},
|
||||
{msg: []byte("oh no!"), validates: false},
|
||||
@ -201,11 +211,11 @@ func TestValidateOverload(t *testing.T) {
|
||||
|
||||
for tci, tc := range tcs {
|
||||
t.Run(fmt.Sprintf("%d", tci), func(t *testing.T) {
|
||||
hosts := getNetHosts(t, ctx, 2)
|
||||
psubs := getPubsubs(ctx, hosts)
|
||||
hosts := getDefaultHosts(t, 2)
|
||||
psubs := getBlossomSubs(ctx, hosts)
|
||||
|
||||
connect(t, hosts[0], hosts[1])
|
||||
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||||
bitmask := []byte{0x00, 0x01}
|
||||
|
||||
block := make(chan struct{})
|
||||
|
||||
@ -232,13 +242,17 @@ func TestValidateOverload(t *testing.T) {
|
||||
}
|
||||
|
||||
p := psubs[0]
|
||||
b, err := p.Join(bitmask)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for _, tmsg := range tc.msgs {
|
||||
select {
|
||||
case msg := <-sub.ch:
|
||||
case msg := <-sub[0].ch:
|
||||
if !tmsg.validates {
|
||||
t.Log(msg)
|
||||
t.Error("expected message validation to drop the message because all validator goroutines are taken")
|
||||
@ -253,7 +267,7 @@ func TestValidateOverload(t *testing.T) {
|
||||
}()
|
||||
|
||||
for _, tmsg := range tc.msgs {
|
||||
err := p.Publish(bitmask, tmsg.msg)
|
||||
err := b[0].Publish(ctx, b[0].bitmask, tmsg.msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -273,8 +287,8 @@ func TestValidateAssortedOptions(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
hosts := getNetHosts(t, ctx, 10)
|
||||
psubs := getPubsubs(ctx, hosts,
|
||||
hosts := getDefaultHosts(t, 10)
|
||||
psubs := getBlossomSubs(ctx, hosts,
|
||||
WithValidateQueueSize(10),
|
||||
WithValidateThrottle(10),
|
||||
WithValidateWorkers(10))
|
||||
@ -282,7 +296,7 @@ func TestValidateAssortedOptions(t *testing.T) {
|
||||
sparseConnect(t, hosts)
|
||||
|
||||
for _, psub := range psubs {
|
||||
err := psub.RegisterBitmaskValidator([]byte{0xff, 0x00, 0x00, 0x00},
|
||||
err := psub.RegisterBitmaskValidator([]byte{0x00, 0x80, 0x00, 0x00},
|
||||
func(context.Context, peer.ID, *Message) bool {
|
||||
return true
|
||||
},
|
||||
@ -291,7 +305,7 @@ func TestValidateAssortedOptions(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = psub.RegisterBitmaskValidator([]byte{0x00, 0xff, 0x00, 0x00},
|
||||
err = psub.RegisterBitmaskValidator([]byte{0x00, 0x20, 0x00, 0x00},
|
||||
func(context.Context, peer.ID, *Message) bool {
|
||||
return true
|
||||
},
|
||||
@ -302,31 +316,44 @@ func TestValidateAssortedOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
var subs1, subs2 []*Subscription
|
||||
var bitmasks1, bitmasks2 []*Bitmask
|
||||
for _, ps := range psubs {
|
||||
sub, err := ps.Subscribe([]byte{0xff, 0x00, 0x00, 0x00})
|
||||
b, err := ps.Join([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs1 = append(subs1, sub)
|
||||
bitmasks1 = append(bitmasks1, b...)
|
||||
|
||||
sub, err = ps.Subscribe([]byte{0x00, 0xff, 0x00, 0x00})
|
||||
b, err = ps.Join([]byte{0x00, 0x04, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs2 = append(subs2, sub)
|
||||
bitmasks2 = append(bitmasks2, b...)
|
||||
sub, err := ps.Subscribe([]byte{0x00, 0x80, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs1 = append(subs1, sub...)
|
||||
|
||||
sub, err = ps.Subscribe([]byte{0x00, 0x04, 0x00, 0x00})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subs2 = append(subs2, sub...)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
msg := []byte(fmt.Sprintf("message %d", i))
|
||||
msg := []byte(fmt.Sprintf("message1 %d", i))
|
||||
|
||||
psubs[i].Publish([]byte{0xff, 0x00, 0x00, 0x00}, msg)
|
||||
bitmasks1[i].Publish(ctx, bitmasks1[i].bitmask, msg)
|
||||
for _, sub := range subs1 {
|
||||
assertReceive(t, sub, msg)
|
||||
}
|
||||
msg = []byte(fmt.Sprintf("message2 %d", i))
|
||||
|
||||
psubs[i].Publish([]byte{0x00, 0xff, 0x00, 0x00}, msg)
|
||||
bitmasks2[i].Publish(ctx, bitmasks2[i].bitmask, msg)
|
||||
for _, sub := range subs2 {
|
||||
assertReceive(t, sub, msg)
|
||||
}
|
||||
|
15
go-libp2p-kad-dht/CODEOWNERS
Normal file
15
go-libp2p-kad-dht/CODEOWNERS
Normal file
@ -0,0 +1,15 @@
|
||||
# CODEOWNERS
|
||||
|
||||
# default owner is the libp2p team
|
||||
*.go @libp2p/go-libp2p-maintainers @guillaumemichel
|
||||
/pb/ @libp2p/go-libp2p-maintainers @guillaumemichel
|
||||
|
||||
# dual is an application for IPFS
|
||||
/dual/ @libp2p/kubo-maintainers @guillaumemichel
|
||||
# fullrt is IPFS specific
|
||||
/fullrt/ @libp2p/kubo-maintainers @guillaumemichel
|
||||
# providers describe the IPFS specific providers
|
||||
/providers/ @libp2p/kubo-maintainers @guillaumemichel
|
||||
# records are IPFS specific
|
||||
/records.go @libp2p/kubo-maintainers @guillaumemichel
|
||||
/records_test.go @libp2p/kubo-maintainers @guillaumemichel
|
21
go-libp2p-kad-dht/LICENSE
Normal file
21
go-libp2p-kad-dht/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Protocol Labs, Inc.
|
||||
|
||||
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.
|
51
go-libp2p-kad-dht/README.md
Normal file
51
go-libp2p-kad-dht/README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# go-libp2p-kad-dht
|
||||
|
||||
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai)
|
||||
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](https://libp2p.io)
|
||||
[![GoDoc](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht?status.svg)](https://godoc.org/github.com/libp2p/go-libp2p-kad-dht)
|
||||
[![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
|
||||
|
||||
> A Go implementation of [libp2p Kademlia DHT specification](https://github.com/libp2p/specs/tree/master/kad-dht)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Optimizations](#optimizations)
|
||||
- [Contribute](#contribute)
|
||||
- [Maintainers](#maintainers)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/libp2p/go-libp2p-kad-dht
|
||||
```
|
||||
|
||||
## Optimizations
|
||||
|
||||
Client-side optimizations are described in [optimizations.md](./optimizations.md)
|
||||
|
||||
## Usage
|
||||
|
||||
Go to https://godoc.org/github.com/libp2p/go-libp2p-kad-dht.
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome. Please check out [the issues](https://github.com/libp2p/go-libp2p-kad-dht/issues).
|
||||
|
||||
Check out our [contributing document](https://github.com/libp2p/community/blob/master/CONTRIBUTE.md) for more information on how we work, and about contributing in general. Please be aware that all interactions related to libp2p are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
|
||||
|
||||
<!-- Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. -->
|
||||
|
||||
## Maintainers
|
||||
|
||||
- [@ipfs/kubo-maintainers](https://github.com/orgs/ipfs/teams/kubo-maintainers)
|
||||
- [@libp2p/go-libp2p-maintainers](https://github.com/orgs/libp2p/teams/go-libp2p-maintainers)
|
||||
- [@guillaumemichel](https://github.com/guillaumemichel)
|
||||
|
||||
See [CODEOWNERS](./CODEOWNERS).
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE) © Protocol Labs Inc.
|
266
go-libp2p-kad-dht/crawler/crawler.go
Normal file
266
go-libp2p-kad-dht/crawler/crawler.go
Normal file
@ -0,0 +1,266 @@
|
||||
package crawler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
//lint:ignore SA1019 TODO migrate away from gogo pb
|
||||
"github.com/libp2p/go-msgio/protoio"
|
||||
|
||||
pb "github.com/libp2p/go-libp2p-kad-dht/pb"
|
||||
kbucket "github.com/libp2p/go-libp2p-kbucket"
|
||||
)
|
||||
|
||||
var (
|
||||
logger = logging.Logger("dht-crawler")
|
||||
|
||||
_ Crawler = (*DefaultCrawler)(nil)
|
||||
)
|
||||
|
||||
type (
|
||||
// Crawler connects to hosts in the DHT to track routing tables of peers.
|
||||
Crawler interface {
|
||||
// Run crawls the DHT starting from the startingPeers, and calls either handleSuccess or handleFail depending on whether a peer was successfully contacted or not.
|
||||
Run(ctx context.Context, startingPeers []*peer.AddrInfo, handleSuccess HandleQueryResult, handleFail HandleQueryFail)
|
||||
}
|
||||
// DefaultCrawler provides a default implementation of Crawler.
|
||||
DefaultCrawler struct {
|
||||
parallelism int
|
||||
connectTimeout time.Duration
|
||||
host host.Host
|
||||
dhtRPC *pb.ProtocolMessenger
|
||||
dialAddressExtendDur time.Duration
|
||||
}
|
||||
)
|
||||
|
||||
// NewDefaultCrawler creates a new DefaultCrawler
|
||||
func NewDefaultCrawler(host host.Host, opts ...Option) (*DefaultCrawler, error) {
|
||||
o := new(options)
|
||||
if err := defaults(o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, opt := range opts {
|
||||
if err := opt(o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pm, err := pb.NewProtocolMessenger(&messageSender{h: host, protocols: o.protocols, timeout: o.perMsgTimeout})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DefaultCrawler{
|
||||
parallelism: o.parallelism,
|
||||
connectTimeout: o.connectTimeout,
|
||||
host: host,
|
||||
dhtRPC: pm,
|
||||
dialAddressExtendDur: o.dialAddressExtendDur,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MessageSender handles sending wire protocol messages to a given peer
|
||||
type messageSender struct {
|
||||
h host.Host
|
||||
protocols []protocol.ID
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// SendRequest sends a peer a message and waits for its response
|
||||
func (ms *messageSender) SendRequest(ctx context.Context, p peer.ID, pmes *pb.Message) (*pb.Message, error) {
|
||||
s, err := ms.h.NewStream(ctx, p, ms.protocols...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := protoio.NewDelimitedWriter(s)
|
||||
if err := w.WriteMsg(pmes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := protoio.NewDelimitedReader(s, network.MessageSizeMax)
|
||||
tctx, cancel := context.WithTimeout(ctx, ms.timeout)
|
||||
defer cancel()
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
msg := new(pb.Message)
|
||||
if err := ctxReadMsg(tctx, r, msg); err != nil {
|
||||
_ = s.Reset()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func ctxReadMsg(ctx context.Context, rc protoio.ReadCloser, mes *pb.Message) error {
|
||||
errc := make(chan error, 1)
|
||||
go func(r protoio.ReadCloser) {
|
||||
defer close(errc)
|
||||
err := r.ReadMsg(mes)
|
||||
errc <- err
|
||||
}(rc)
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessage sends a peer a message without waiting on a response
|
||||
func (ms *messageSender) SendMessage(ctx context.Context, p peer.ID, pmes *pb.Message) error {
|
||||
s, err := ms.h.NewStream(ctx, p, ms.protocols...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = s.Close() }()
|
||||
|
||||
w := protoio.NewDelimitedWriter(s)
|
||||
return w.WriteMsg(pmes)
|
||||
}
|
||||
|
||||
// HandleQueryResult is a callback on successful peer query
|
||||
type HandleQueryResult func(p peer.ID, rtPeers []*peer.AddrInfo)
|
||||
|
||||
// HandleQueryFail is a callback on failed peer query
|
||||
type HandleQueryFail func(p peer.ID, err error)
|
||||
|
||||
// Run crawls dht peers from an initial seed of `startingPeers`
|
||||
func (c *DefaultCrawler) Run(ctx context.Context, startingPeers []*peer.AddrInfo, handleSuccess HandleQueryResult, handleFail HandleQueryFail) {
|
||||
jobs := make(chan peer.ID, 1)
|
||||
results := make(chan *queryResult, 1)
|
||||
|
||||
// Start worker goroutines
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(c.parallelism)
|
||||
for i := 0; i < c.parallelism; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for p := range jobs {
|
||||
res := c.queryPeer(ctx, p)
|
||||
results <- res
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
defer wg.Wait()
|
||||
defer close(jobs)
|
||||
|
||||
var toDial []*peer.AddrInfo
|
||||
peersSeen := make(map[peer.ID]struct{})
|
||||
|
||||
numSkipped := 0
|
||||
for _, ai := range startingPeers {
|
||||
extendAddrs := c.host.Peerstore().Addrs(ai.ID)
|
||||
if len(ai.Addrs) > 0 {
|
||||
extendAddrs = append(extendAddrs, ai.Addrs...)
|
||||
c.host.Peerstore().AddAddrs(ai.ID, extendAddrs, c.dialAddressExtendDur)
|
||||
}
|
||||
if len(extendAddrs) == 0 {
|
||||
numSkipped++
|
||||
continue
|
||||
}
|
||||
|
||||
toDial = append(toDial, ai)
|
||||
peersSeen[ai.ID] = struct{}{}
|
||||
}
|
||||
|
||||
if numSkipped > 0 {
|
||||
logger.Infof("%d starting peers were skipped due to lack of addresses. Starting crawl with %d peers", numSkipped, len(toDial))
|
||||
}
|
||||
|
||||
numQueried := 0
|
||||
outstanding := 0
|
||||
|
||||
for len(toDial) > 0 || outstanding > 0 {
|
||||
var jobCh chan peer.ID
|
||||
var nextPeerID peer.ID
|
||||
if len(toDial) > 0 {
|
||||
jobCh = jobs
|
||||
nextPeerID = toDial[0].ID
|
||||
}
|
||||
|
||||
select {
|
||||
case res := <-results:
|
||||
if len(res.data) > 0 {
|
||||
logger.Debugf("peer %v had %d peers", res.peer, len(res.data))
|
||||
rtPeers := make([]*peer.AddrInfo, 0, len(res.data))
|
||||
for p, ai := range res.data {
|
||||
c.host.Peerstore().AddAddrs(p, ai.Addrs, c.dialAddressExtendDur)
|
||||
if _, ok := peersSeen[p]; !ok {
|
||||
peersSeen[p] = struct{}{}
|
||||
toDial = append(toDial, ai)
|
||||
}
|
||||
rtPeers = append(rtPeers, ai)
|
||||
}
|
||||
if handleSuccess != nil {
|
||||
handleSuccess(res.peer, rtPeers)
|
||||
}
|
||||
} else if handleFail != nil {
|
||||
handleFail(res.peer, res.err)
|
||||
}
|
||||
outstanding--
|
||||
case jobCh <- nextPeerID:
|
||||
outstanding++
|
||||
numQueried++
|
||||
toDial = toDial[1:]
|
||||
logger.Debugf("starting %d out of %d", numQueried, len(peersSeen))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
peer peer.ID
|
||||
data map[peer.ID]*peer.AddrInfo
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *DefaultCrawler) queryPeer(ctx context.Context, nextPeer peer.ID) *queryResult {
|
||||
tmpRT, err := kbucket.NewRoutingTable(20, kbucket.ConvertPeerID(nextPeer), time.Hour, c.host.Peerstore(), time.Hour, nil)
|
||||
if err != nil {
|
||||
logger.Errorf("error creating rt for peer %v : %v", nextPeer, err)
|
||||
return &queryResult{nextPeer, nil, err}
|
||||
}
|
||||
|
||||
connCtx, cancel := context.WithTimeout(ctx, c.connectTimeout)
|
||||
defer cancel()
|
||||
err = c.host.Connect(connCtx, peer.AddrInfo{ID: nextPeer})
|
||||
if err != nil {
|
||||
logger.Debugf("could not connect to peer %v: %v", nextPeer, err)
|
||||
return &queryResult{nextPeer, nil, err}
|
||||
}
|
||||
|
||||
localPeers := make(map[peer.ID]*peer.AddrInfo)
|
||||
var retErr error
|
||||
for cpl := 0; cpl <= 15; cpl++ {
|
||||
generatePeer, err := tmpRT.GenRandPeerID(uint(cpl))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
peers, err := c.dhtRPC.GetClosestPeers(ctx, nextPeer, generatePeer)
|
||||
if err != nil {
|
||||
logger.Debugf("error finding data on peer %v with cpl %d : %v", nextPeer, cpl, err)
|
||||
retErr = err
|
||||
break
|
||||
}
|
||||
for _, ai := range peers {
|
||||
if _, ok := localPeers[ai.ID]; !ok {
|
||||
localPeers[ai.ID] = ai
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if retErr != nil {
|
||||
return &queryResult{nextPeer, nil, retErr}
|
||||
}
|
||||
|
||||
return &queryResult{nextPeer, localPeers, retErr}
|
||||
}
|
72
go-libp2p-kad-dht/crawler/options.go
Normal file
72
go-libp2p-kad-dht/crawler/options.go
Normal file
@ -0,0 +1,72 @@
|
||||
package crawler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
)
|
||||
|
||||
// Option DHT Crawler option type.
|
||||
type Option func(*options) error
|
||||
|
||||
type options struct {
|
||||
protocols []protocol.ID
|
||||
parallelism int
|
||||
connectTimeout time.Duration
|
||||
perMsgTimeout time.Duration
|
||||
dialAddressExtendDur time.Duration
|
||||
}
|
||||
|
||||
// defaults are the default crawler options. This option will be automatically
|
||||
// prepended to any options you pass to the crawler constructor.
|
||||
var defaults = func(o *options) error {
|
||||
o.protocols = []protocol.ID{"/ipfs/kad/1.0.0"}
|
||||
o.parallelism = 1000
|
||||
o.connectTimeout = time.Second * 5
|
||||
o.perMsgTimeout = time.Second * 5
|
||||
o.dialAddressExtendDur = time.Minute * 30
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithProtocols defines the ordered set of protocols the crawler will use to talk to other nodes
|
||||
func WithProtocols(protocols []protocol.ID) Option {
|
||||
return func(o *options) error {
|
||||
o.protocols = append([]protocol.ID{}, protocols...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithParallelism defines the number of queries that can be issued in parallel
|
||||
func WithParallelism(parallelism int) Option {
|
||||
return func(o *options) error {
|
||||
o.parallelism = parallelism
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithMsgTimeout defines the amount of time a single DHT message is allowed to take before it's deemed failed
|
||||
func WithMsgTimeout(timeout time.Duration) Option {
|
||||
return func(o *options) error {
|
||||
o.perMsgTimeout = timeout
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithConnectTimeout defines the time for peer connection before timing out
|
||||
func WithConnectTimeout(timeout time.Duration) Option {
|
||||
return func(o *options) error {
|
||||
o.connectTimeout = timeout
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDialAddrExtendDuration sets the duration by which the TTL of dialed address in peer store are
|
||||
// extended.
|
||||
// Defaults to 30 minutes if unset.
|
||||
func WithDialAddrExtendDuration(ext time.Duration) Option {
|
||||
return func(o *options) error {
|
||||
o.dialAddressExtendDur = ext
|
||||
return nil
|
||||
}
|
||||
}
|
942
go-libp2p-kad-dht/dht.go
Normal file
942
go-libp2p-kad-dht/dht.go
Normal file
@ -0,0 +1,942 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-routing-helpers/tracing"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
"github.com/libp2p/go-libp2p/core/routing"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/libp2p/go-libp2p-kad-dht/internal"
|
||||
dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/internal/net"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/metrics"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/netsize"
|
||||
pb "github.com/libp2p/go-libp2p-kad-dht/pb"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/providers"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/rtrefresh"
|
||||
kb "github.com/libp2p/go-libp2p-kbucket"
|
||||
"github.com/libp2p/go-libp2p-kbucket/peerdiversity"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
recpb "github.com/libp2p/go-libp2p-record/pb"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/multiformats/go-base32"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"go.opencensus.io/tag"
|
||||
"go.uber.org/multierr"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const tracer = tracing.Tracer("go-libp2p-kad-dht")
|
||||
const dhtName = "IpfsDHT"
|
||||
|
||||
var (
|
||||
logger = logging.Logger("dht")
|
||||
baseLogger = logger.Desugar()
|
||||
|
||||
rtFreezeTimeout = 1 * time.Minute
|
||||
)
|
||||
|
||||
const (
|
||||
// BaseConnMgrScore is the base of the score set on the connection
|
||||
// manager "kbucket" tag. It is added with the common prefix length
|
||||
// between two peer IDs.
|
||||
baseConnMgrScore = 5
|
||||
)
|
||||
|
||||
type mode int
|
||||
|
||||
const (
|
||||
modeServer mode = iota + 1
|
||||
modeClient
|
||||
)
|
||||
|
||||
const (
|
||||
kad1 protocol.ID = "/kad/1.0.0"
|
||||
)
|
||||
|
||||
const (
|
||||
kbucketTag = "kbucket"
|
||||
protectedBuckets = 2
|
||||
)
|
||||
|
||||
// IpfsDHT is an implementation of Kademlia with S/Kademlia modifications.
|
||||
// It is used to implement the base Routing module.
|
||||
type IpfsDHT struct {
|
||||
host host.Host // the network services we need
|
||||
self peer.ID // Local peer (yourself)
|
||||
selfKey kb.ID
|
||||
peerstore peerstore.Peerstore // Peer Registry
|
||||
|
||||
datastore ds.Datastore // Local data
|
||||
|
||||
routingTable *kb.RoutingTable // Array of routing tables for differently distanced nodes
|
||||
// providerStore stores & manages the provider records for this Dht peer.
|
||||
providerStore providers.ProviderStore
|
||||
|
||||
// manages Routing Table refresh
|
||||
rtRefreshManager *rtrefresh.RtRefreshManager
|
||||
|
||||
birth time.Time // When this peer started up
|
||||
|
||||
Validator record.Validator
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
|
||||
protoMessenger *pb.ProtocolMessenger
|
||||
msgSender pb.MessageSenderWithDisconnect
|
||||
|
||||
stripedPutLocks [256]sync.Mutex
|
||||
|
||||
// DHT protocols we query with. We'll only add peers to our routing
|
||||
// table if they speak these protocols.
|
||||
protocols []protocol.ID
|
||||
|
||||
// DHT protocols we can respond to.
|
||||
serverProtocols []protocol.ID
|
||||
|
||||
auto ModeOpt
|
||||
mode mode
|
||||
modeLk sync.Mutex
|
||||
|
||||
bucketSize int
|
||||
alpha int // The concurrency parameter per path
|
||||
beta int // The number of peers closest to a target that must have responded for a query path to terminate
|
||||
|
||||
queryPeerFilter QueryFilterFunc
|
||||
routingTablePeerFilter RouteTableFilterFunc
|
||||
rtPeerDiversityFilter peerdiversity.PeerIPGroupFilter
|
||||
|
||||
autoRefresh bool
|
||||
|
||||
// timeout for the lookupCheck operation
|
||||
lookupCheckTimeout time.Duration
|
||||
// number of concurrent lookupCheck operations
|
||||
lookupCheckCapacity int
|
||||
lookupChecksLk sync.Mutex
|
||||
|
||||
// A function returning a set of bootstrap peers to fallback on if all other attempts to fix
|
||||
// the routing table fail (or, e.g., this is the first time this node is
|
||||
// connecting to the network).
|
||||
bootstrapPeers func() []peer.AddrInfo
|
||||
|
||||
maxRecordAge time.Duration
|
||||
|
||||
// Allows disabling dht subsystems. These should _only_ be set on
|
||||
// "forked" DHTs (e.g., DHTs with custom protocols and/or private
|
||||
// networks).
|
||||
enableProviders, enableValues bool
|
||||
|
||||
disableFixLowPeers bool
|
||||
fixLowPeersChan chan struct{}
|
||||
|
||||
addPeerToRTChan chan peer.ID
|
||||
refreshFinishedCh chan struct{}
|
||||
|
||||
rtFreezeTimeout time.Duration
|
||||
|
||||
// network size estimator
|
||||
nsEstimator *netsize.Estimator
|
||||
enableOptProv bool
|
||||
|
||||
// a bound channel to limit asynchronicity of in-flight ADD_PROVIDER RPCs
|
||||
optProvJobsPool chan struct{}
|
||||
|
||||
// configuration variables for tests
|
||||
testAddressUpdateProcessing bool
|
||||
|
||||
// addrFilter is used to filter the addresses we put into the peer store.
|
||||
// Mostly used to filter out localhost and local addresses.
|
||||
addrFilter func([]ma.Multiaddr) []ma.Multiaddr
|
||||
}
|
||||
|
||||
// Assert that IPFS assumptions about interfaces aren't broken. These aren't a
|
||||
// guarantee, but we can use them to aid refactoring.
|
||||
var (
|
||||
_ routing.ContentRouting = (*IpfsDHT)(nil)
|
||||
_ routing.Routing = (*IpfsDHT)(nil)
|
||||
_ routing.PeerRouting = (*IpfsDHT)(nil)
|
||||
_ routing.PubKeyFetcher = (*IpfsDHT)(nil)
|
||||
_ routing.ValueStore = (*IpfsDHT)(nil)
|
||||
)
|
||||
|
||||
// New creates a new DHT with the specified host and options.
|
||||
// Please note that being connected to a DHT peer does not necessarily imply that it's also in the DHT Routing Table.
|
||||
// If the Routing Table has more than "minRTRefreshThreshold" peers, we consider a peer as a Routing Table candidate ONLY when
|
||||
// we successfully get a query response from it OR if it send us a query.
|
||||
func New(ctx context.Context, h host.Host, options ...Option) (*IpfsDHT, error) {
|
||||
var cfg dhtcfg.Config
|
||||
if err := cfg.Apply(append([]Option{dhtcfg.Defaults}, options...)...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := cfg.ApplyFallbacks(h); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dht, err := makeDHT(h, cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create DHT, err=%s", err)
|
||||
}
|
||||
|
||||
dht.autoRefresh = cfg.RoutingTable.AutoRefresh
|
||||
|
||||
dht.maxRecordAge = cfg.MaxRecordAge
|
||||
dht.enableProviders = cfg.EnableProviders
|
||||
dht.enableValues = cfg.EnableValues
|
||||
dht.disableFixLowPeers = cfg.DisableFixLowPeers
|
||||
|
||||
dht.Validator = cfg.Validator
|
||||
dht.msgSender = net.NewMessageSenderImpl(h, dht.protocols)
|
||||
dht.protoMessenger, err = pb.NewProtocolMessenger(dht.msgSender)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dht.testAddressUpdateProcessing = cfg.TestAddressUpdateProcessing
|
||||
|
||||
dht.auto = cfg.Mode
|
||||
switch cfg.Mode {
|
||||
case ModeAuto, ModeClient:
|
||||
dht.mode = modeClient
|
||||
case ModeAutoServer, ModeServer:
|
||||
dht.mode = modeServer
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid dht mode %d", cfg.Mode)
|
||||
}
|
||||
|
||||
if dht.mode == modeServer {
|
||||
if err := dht.moveToServerMode(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// register for event bus and network notifications
|
||||
if err := dht.startNetworkSubscriber(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// go-routine to make sure we ALWAYS have RT peer addresses in the peerstore
|
||||
// since RT membership is decoupled from connectivity
|
||||
go dht.persistRTPeersInPeerStore()
|
||||
|
||||
dht.rtPeerLoop()
|
||||
|
||||
// Fill routing table with currently connected peers that are DHT servers
|
||||
for _, p := range dht.host.Network().Peers() {
|
||||
dht.peerFound(p)
|
||||
}
|
||||
|
||||
dht.rtRefreshManager.Start()
|
||||
|
||||
// listens to the fix low peers chan and tries to fix the Routing Table
|
||||
if !dht.disableFixLowPeers {
|
||||
dht.runFixLowPeersLoop()
|
||||
}
|
||||
|
||||
return dht, nil
|
||||
}
|
||||
|
||||
// NewDHT creates a new DHT object with the given peer as the 'local' host.
|
||||
// IpfsDHT's initialized with this function will respond to DHT requests,
|
||||
// whereas IpfsDHT's initialized with NewDHTClient will not.
|
||||
func NewDHT(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT {
|
||||
dht, err := New(ctx, h, Datastore(dstore))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dht
|
||||
}
|
||||
|
||||
// NewDHTClient creates a new DHT object with the given peer as the 'local'
|
||||
// host. IpfsDHT clients initialized with this function will not respond to DHT
|
||||
// requests. If you need a peer to respond to DHT requests, use NewDHT instead.
|
||||
func NewDHTClient(ctx context.Context, h host.Host, dstore ds.Batching) *IpfsDHT {
|
||||
dht, err := New(ctx, h, Datastore(dstore), Mode(ModeClient))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dht
|
||||
}
|
||||
|
||||
func makeDHT(h host.Host, cfg dhtcfg.Config) (*IpfsDHT, error) {
|
||||
var protocols, serverProtocols []protocol.ID
|
||||
|
||||
v1proto := cfg.ProtocolPrefix + kad1
|
||||
|
||||
if cfg.V1ProtocolOverride != "" {
|
||||
v1proto = cfg.V1ProtocolOverride
|
||||
}
|
||||
|
||||
protocols = []protocol.ID{v1proto}
|
||||
serverProtocols = []protocol.ID{v1proto}
|
||||
|
||||
dht := &IpfsDHT{
|
||||
datastore: cfg.Datastore,
|
||||
self: h.ID(),
|
||||
selfKey: kb.ConvertPeerID(h.ID()),
|
||||
peerstore: h.Peerstore(),
|
||||
host: h,
|
||||
birth: time.Now(),
|
||||
protocols: protocols,
|
||||
serverProtocols: serverProtocols,
|
||||
bucketSize: cfg.BucketSize,
|
||||
alpha: cfg.Concurrency,
|
||||
beta: cfg.Resiliency,
|
||||
lookupCheckCapacity: cfg.LookupCheckConcurrency,
|
||||
queryPeerFilter: cfg.QueryPeerFilter,
|
||||
routingTablePeerFilter: cfg.RoutingTable.PeerFilter,
|
||||
rtPeerDiversityFilter: cfg.RoutingTable.DiversityFilter,
|
||||
addrFilter: cfg.AddressFilter,
|
||||
|
||||
fixLowPeersChan: make(chan struct{}, 1),
|
||||
|
||||
addPeerToRTChan: make(chan peer.ID),
|
||||
refreshFinishedCh: make(chan struct{}),
|
||||
|
||||
enableOptProv: cfg.EnableOptimisticProvide,
|
||||
optProvJobsPool: nil,
|
||||
}
|
||||
|
||||
var maxLastSuccessfulOutboundThreshold time.Duration
|
||||
|
||||
// The threshold is calculated based on the expected amount of time that should pass before we
|
||||
// query a peer as part of our refresh cycle.
|
||||
// To grok the Math Wizardy that produced these exact equations, please be patient as a document explaining it will
|
||||
// be published soon.
|
||||
if cfg.Concurrency < cfg.BucketSize { // (alpha < K)
|
||||
l1 := math.Log(float64(1) / float64(cfg.BucketSize)) // (Log(1/K))
|
||||
l2 := math.Log(float64(1) - (float64(cfg.Concurrency) / float64(cfg.BucketSize))) // Log(1 - (alpha / K))
|
||||
maxLastSuccessfulOutboundThreshold = time.Duration(l1 / l2 * float64(cfg.RoutingTable.RefreshInterval))
|
||||
} else {
|
||||
maxLastSuccessfulOutboundThreshold = cfg.RoutingTable.RefreshInterval
|
||||
}
|
||||
|
||||
// construct routing table
|
||||
// use twice the theoritical usefulness threhold to keep older peers around longer
|
||||
rt, err := makeRoutingTable(dht, cfg, 2*maxLastSuccessfulOutboundThreshold)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct routing table,err=%s", err)
|
||||
}
|
||||
dht.routingTable = rt
|
||||
dht.bootstrapPeers = cfg.BootstrapPeers
|
||||
|
||||
dht.lookupCheckTimeout = cfg.RoutingTable.RefreshQueryTimeout
|
||||
|
||||
// init network size estimator
|
||||
dht.nsEstimator = netsize.NewEstimator(h.ID(), rt, cfg.BucketSize)
|
||||
|
||||
if dht.enableOptProv {
|
||||
dht.optProvJobsPool = make(chan struct{}, cfg.OptimisticProvideJobsPoolSize)
|
||||
}
|
||||
|
||||
// rt refresh manager
|
||||
dht.rtRefreshManager, err = makeRtRefreshManager(dht, cfg, maxLastSuccessfulOutboundThreshold)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct RT Refresh Manager,err=%s", err)
|
||||
}
|
||||
|
||||
// create a tagged context derived from the original context
|
||||
// the DHT context should be done when the process is closed
|
||||
dht.ctx, dht.cancel = context.WithCancel(dht.newContextWithLocalTags(context.Background()))
|
||||
|
||||
if cfg.ProviderStore != nil {
|
||||
dht.providerStore = cfg.ProviderStore
|
||||
} else {
|
||||
dht.providerStore, err = providers.NewProviderManager(h.ID(), dht.peerstore, cfg.Datastore)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing default provider manager (%v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
dht.rtFreezeTimeout = rtFreezeTimeout
|
||||
|
||||
return dht, nil
|
||||
}
|
||||
|
||||
// lookupCheck performs a lookup request to a remote peer.ID, verifying that it is able to
|
||||
// answer it correctly
|
||||
func (dht *IpfsDHT) lookupCheck(ctx context.Context, p peer.ID) error {
|
||||
// lookup request to p requesting for its own peer.ID
|
||||
peerids, err := dht.protoMessenger.GetClosestPeers(ctx, p, p)
|
||||
// p is expected to return at least 1 peer id, unless our routing table has
|
||||
// less than bucketSize peers, in which case we aren't picky about who we
|
||||
// add to the routing table.
|
||||
if err == nil && len(peerids) == 0 && dht.routingTable.Size() >= dht.bucketSize {
|
||||
return fmt.Errorf("peer %s failed to return its closest peers, got %d", p, len(peerids))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func makeRtRefreshManager(dht *IpfsDHT, cfg dhtcfg.Config, maxLastSuccessfulOutboundThreshold time.Duration) (*rtrefresh.RtRefreshManager, error) {
|
||||
keyGenFnc := func(cpl uint) (string, error) {
|
||||
p, err := dht.routingTable.GenRandPeerID(cpl)
|
||||
return string(p), err
|
||||
}
|
||||
|
||||
queryFnc := func(ctx context.Context, key string) error {
|
||||
_, err := dht.GetClosestPeers(ctx, key)
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := rtrefresh.NewRtRefreshManager(
|
||||
dht.host, dht.routingTable, cfg.RoutingTable.AutoRefresh,
|
||||
keyGenFnc,
|
||||
queryFnc,
|
||||
dht.lookupCheck,
|
||||
cfg.RoutingTable.RefreshQueryTimeout,
|
||||
cfg.RoutingTable.RefreshInterval,
|
||||
maxLastSuccessfulOutboundThreshold,
|
||||
dht.refreshFinishedCh)
|
||||
|
||||
return r, err
|
||||
}
|
||||
|
||||
func makeRoutingTable(dht *IpfsDHT, cfg dhtcfg.Config, maxLastSuccessfulOutboundThreshold time.Duration) (*kb.RoutingTable, error) {
|
||||
// make a Routing Table Diversity Filter
|
||||
var filter *peerdiversity.Filter
|
||||
if dht.rtPeerDiversityFilter != nil {
|
||||
df, err := peerdiversity.NewFilter(dht.rtPeerDiversityFilter, "rt/diversity", func(p peer.ID) int {
|
||||
return kb.CommonPrefixLen(dht.selfKey, kb.ConvertPeerID(p))
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct peer diversity filter: %w", err)
|
||||
}
|
||||
|
||||
filter = df
|
||||
}
|
||||
|
||||
rt, err := kb.NewRoutingTable(cfg.BucketSize, dht.selfKey, time.Minute, dht.host.Peerstore(), maxLastSuccessfulOutboundThreshold, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmgr := dht.host.ConnManager()
|
||||
|
||||
rt.PeerAdded = func(p peer.ID) {
|
||||
commonPrefixLen := kb.CommonPrefixLen(dht.selfKey, kb.ConvertPeerID(p))
|
||||
if commonPrefixLen < protectedBuckets {
|
||||
cmgr.Protect(p, kbucketTag)
|
||||
} else {
|
||||
cmgr.TagPeer(p, kbucketTag, baseConnMgrScore)
|
||||
}
|
||||
}
|
||||
rt.PeerRemoved = func(p peer.ID) {
|
||||
cmgr.Unprotect(p, kbucketTag)
|
||||
cmgr.UntagPeer(p, kbucketTag)
|
||||
|
||||
// try to fix the RT
|
||||
dht.fixRTIfNeeded()
|
||||
}
|
||||
|
||||
return rt, err
|
||||
}
|
||||
|
||||
// ProviderStore returns the provider storage object for storing and retrieving provider records.
|
||||
func (dht *IpfsDHT) ProviderStore() providers.ProviderStore {
|
||||
return dht.providerStore
|
||||
}
|
||||
|
||||
// GetRoutingTableDiversityStats returns the diversity stats for the Routing Table.
|
||||
func (dht *IpfsDHT) GetRoutingTableDiversityStats() []peerdiversity.CplDiversityStats {
|
||||
return dht.routingTable.GetDiversityStats()
|
||||
}
|
||||
|
||||
// Mode allows introspection of the operation mode of the DHT
|
||||
func (dht *IpfsDHT) Mode() ModeOpt {
|
||||
return dht.auto
|
||||
}
|
||||
|
||||
// runFixLowPeersLoop manages simultaneous requests to fixLowPeers
|
||||
func (dht *IpfsDHT) runFixLowPeersLoop() {
|
||||
dht.wg.Add(1)
|
||||
go func() {
|
||||
defer dht.wg.Done()
|
||||
|
||||
dht.fixLowPeers()
|
||||
|
||||
ticker := time.NewTicker(periodicBootstrapInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-dht.fixLowPeersChan:
|
||||
case <-ticker.C:
|
||||
case <-dht.ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
dht.fixLowPeers()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// fixLowPeers tries to get more peers into the routing table if we're below the threshold
|
||||
func (dht *IpfsDHT) fixLowPeers() {
|
||||
if dht.routingTable.Size() > minRTRefreshThreshold {
|
||||
return
|
||||
}
|
||||
|
||||
// we try to add all peers we are connected to to the Routing Table
|
||||
// in case they aren't already there.
|
||||
for _, p := range dht.host.Network().Peers() {
|
||||
dht.peerFound(p)
|
||||
}
|
||||
|
||||
// TODO Active Bootstrapping
|
||||
// We should first use non-bootstrap peers we knew of from previous
|
||||
// snapshots of the Routing Table before we connect to the bootstrappers.
|
||||
// See https://github.com/libp2p/go-libp2p-kad-dht/issues/387.
|
||||
if dht.routingTable.Size() == 0 && dht.bootstrapPeers != nil {
|
||||
bootstrapPeers := dht.bootstrapPeers()
|
||||
if len(bootstrapPeers) == 0 {
|
||||
// No point in continuing, we have no peers!
|
||||
return
|
||||
}
|
||||
|
||||
found := 0
|
||||
for _, i := range rand.Perm(len(bootstrapPeers)) {
|
||||
ai := bootstrapPeers[i]
|
||||
err := dht.Host().Connect(dht.ctx, ai)
|
||||
if err == nil {
|
||||
found++
|
||||
} else {
|
||||
logger.Warnw("failed to bootstrap", "peer", ai.ID, "error", err)
|
||||
}
|
||||
|
||||
// Wait for two bootstrap peers, or try them all.
|
||||
//
|
||||
// Why two? In theory, one should be enough
|
||||
// normally. However, if the network were to
|
||||
// restart and everyone connected to just one
|
||||
// bootstrapper, we'll end up with a mostly
|
||||
// partitioned network.
|
||||
//
|
||||
// So we always bootstrap with two random peers.
|
||||
if found == maxNBoostrappers {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we still don't have peers in our routing table(probably because Identify hasn't completed),
|
||||
// there is no point in triggering a Refresh.
|
||||
if dht.routingTable.Size() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if dht.autoRefresh {
|
||||
dht.rtRefreshManager.RefreshNoWait()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This is hacky, horrible and the programmer needs to have his mother called a hamster.
|
||||
// SHOULD be removed once https://github.com/libp2p/go-libp2p/issues/800 goes in.
|
||||
func (dht *IpfsDHT) persistRTPeersInPeerStore() {
|
||||
tickr := time.NewTicker(peerstore.RecentlyConnectedAddrTTL / 3)
|
||||
defer tickr.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-tickr.C:
|
||||
ps := dht.routingTable.ListPeers()
|
||||
for _, p := range ps {
|
||||
dht.peerstore.UpdateAddrs(p, peerstore.RecentlyConnectedAddrTTL, peerstore.RecentlyConnectedAddrTTL)
|
||||
}
|
||||
case <-dht.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getLocal attempts to retrieve the value from the datastore.
|
||||
//
|
||||
// returns nil, nil when either nothing is found or the value found doesn't properly validate.
|
||||
// returns nil, some_error when there's a *datastore* error (i.e., something goes very wrong)
|
||||
func (dht *IpfsDHT) getLocal(ctx context.Context, key string) (*recpb.Record, error) {
|
||||
logger.Debugw("finding value in datastore", "key", internal.LoggableRecordKeyString(key))
|
||||
|
||||
rec, err := dht.getRecordFromDatastore(ctx, mkDsKey(key))
|
||||
if err != nil {
|
||||
logger.Warnw("get local failed", "key", internal.LoggableRecordKeyString(key), "error", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Double check the key. Can't hurt.
|
||||
if rec != nil && string(rec.GetKey()) != key {
|
||||
logger.Errorw("BUG: found a DHT record that didn't match it's key", "expected", internal.LoggableRecordKeyString(key), "got", rec.GetKey())
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
return rec, nil
|
||||
}
|
||||
|
||||
// putLocal stores the key value pair in the datastore
|
||||
func (dht *IpfsDHT) putLocal(ctx context.Context, key string, rec *recpb.Record) error {
|
||||
data, err := proto.Marshal(rec)
|
||||
if err != nil {
|
||||
logger.Warnw("failed to put marshal record for local put", "error", err, "key", internal.LoggableRecordKeyString(key))
|
||||
return err
|
||||
}
|
||||
|
||||
return dht.datastore.Put(ctx, mkDsKey(key), data)
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) rtPeerLoop() {
|
||||
dht.wg.Add(1)
|
||||
go func() {
|
||||
defer dht.wg.Done()
|
||||
|
||||
var bootstrapCount uint
|
||||
var isBootsrapping bool
|
||||
var timerCh <-chan time.Time
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timerCh:
|
||||
dht.routingTable.MarkAllPeersIrreplaceable()
|
||||
case p := <-dht.addPeerToRTChan:
|
||||
if dht.routingTable.Size() == 0 {
|
||||
isBootsrapping = true
|
||||
bootstrapCount = 0
|
||||
timerCh = nil
|
||||
}
|
||||
// queryPeer set to true as we only try to add queried peers to the RT
|
||||
newlyAdded, err := dht.routingTable.TryAddPeer(p, true, isBootsrapping)
|
||||
if err != nil {
|
||||
// peer not added.
|
||||
continue
|
||||
}
|
||||
if newlyAdded {
|
||||
// peer was added to the RT, it can now be fixed if needed.
|
||||
dht.fixRTIfNeeded()
|
||||
} else {
|
||||
// the peer is already in our RT, but we just successfully queried it and so let's give it a
|
||||
// bump on the query time so we don't ping it too soon for a liveliness check.
|
||||
dht.routingTable.UpdateLastSuccessfulOutboundQueryAt(p, time.Now())
|
||||
}
|
||||
case <-dht.refreshFinishedCh:
|
||||
bootstrapCount = bootstrapCount + 1
|
||||
if bootstrapCount == 2 {
|
||||
timerCh = time.NewTimer(dht.rtFreezeTimeout).C
|
||||
}
|
||||
|
||||
old := isBootsrapping
|
||||
isBootsrapping = false
|
||||
if old {
|
||||
dht.rtRefreshManager.RefreshNoWait()
|
||||
}
|
||||
|
||||
case <-dht.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// peerFound verifies whether the found peer advertises DHT protocols
|
||||
// and probe it to make sure it answers DHT queries as expected. If
|
||||
// it fails to answer, it isn't added to the routingTable.
|
||||
func (dht *IpfsDHT) peerFound(p peer.ID) {
|
||||
// if the peer is already in the routing table or the appropriate bucket is
|
||||
// already full, don't try to add the new peer.ID
|
||||
if !dht.routingTable.UsefulNewPeer(p) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify whether the remote peer advertises the right dht protocol
|
||||
b, err := dht.validRTPeer(p)
|
||||
if err != nil {
|
||||
logger.Errorw("failed to validate if peer is a DHT peer", "peer", p, "error", err)
|
||||
} else if b {
|
||||
|
||||
// check if the maximal number of concurrent lookup checks is reached
|
||||
dht.lookupChecksLk.Lock()
|
||||
if dht.lookupCheckCapacity == 0 {
|
||||
dht.lookupChecksLk.Unlock()
|
||||
// drop the new peer.ID if the maximal number of concurrent lookup
|
||||
// checks is reached
|
||||
return
|
||||
}
|
||||
dht.lookupCheckCapacity--
|
||||
dht.lookupChecksLk.Unlock()
|
||||
|
||||
go func() {
|
||||
livelinessCtx, cancel := context.WithTimeout(dht.ctx, dht.lookupCheckTimeout)
|
||||
defer cancel()
|
||||
|
||||
// performing a FIND_NODE query
|
||||
err := dht.lookupCheck(livelinessCtx, p)
|
||||
|
||||
dht.lookupChecksLk.Lock()
|
||||
dht.lookupCheckCapacity++
|
||||
dht.lookupChecksLk.Unlock()
|
||||
|
||||
if err != nil {
|
||||
logger.Debugw("connected peer not answering DHT request as expected", "peer", p, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// if the FIND_NODE succeeded, the peer is considered as valid
|
||||
dht.validPeerFound(p)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// validPeerFound signals the routingTable that we've found a peer that
|
||||
// supports the DHT protocol, and just answered correctly to a DHT FindPeers
|
||||
func (dht *IpfsDHT) validPeerFound(p peer.ID) {
|
||||
if c := baseLogger.Check(zap.DebugLevel, "peer found"); c != nil {
|
||||
c.Write(zap.String("peer", p.String()))
|
||||
}
|
||||
|
||||
select {
|
||||
case dht.addPeerToRTChan <- p:
|
||||
case <-dht.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// peerStoppedDHT signals the routing table that a peer is unable to responsd to DHT queries anymore.
|
||||
func (dht *IpfsDHT) peerStoppedDHT(p peer.ID) {
|
||||
logger.Debugw("peer stopped dht", "peer", p)
|
||||
// A peer that does not support the DHT protocol is dead for us.
|
||||
// There's no point in talking to anymore till it starts supporting the DHT protocol again.
|
||||
dht.routingTable.RemovePeer(p)
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) fixRTIfNeeded() {
|
||||
select {
|
||||
case dht.fixLowPeersChan <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// FindLocal looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in.
|
||||
func (dht *IpfsDHT) FindLocal(ctx context.Context, id peer.ID) peer.AddrInfo {
|
||||
_, span := internal.StartSpan(ctx, "IpfsDHT.FindLocal", trace.WithAttributes(attribute.Stringer("PeerID", id)))
|
||||
defer span.End()
|
||||
|
||||
switch dht.host.Network().Connectedness(id) {
|
||||
case network.Connected, network.CanConnect:
|
||||
return dht.peerstore.PeerInfo(id)
|
||||
default:
|
||||
return peer.AddrInfo{}
|
||||
}
|
||||
}
|
||||
|
||||
// nearestPeersToQuery returns the routing tables closest peers.
|
||||
func (dht *IpfsDHT) nearestPeersToQuery(pmes *pb.Message, count int) []peer.ID {
|
||||
closer := dht.routingTable.NearestPeers(kb.ConvertKey(string(pmes.GetKey())), count)
|
||||
return closer
|
||||
}
|
||||
|
||||
// betterPeersToQuery returns nearestPeersToQuery with some additional filtering
|
||||
func (dht *IpfsDHT) betterPeersToQuery(pmes *pb.Message, from peer.ID, count int) []peer.ID {
|
||||
closer := dht.nearestPeersToQuery(pmes, count)
|
||||
|
||||
// no node? nil
|
||||
if closer == nil {
|
||||
logger.Infow("no closer peers to send", from)
|
||||
return nil
|
||||
}
|
||||
|
||||
filtered := make([]peer.ID, 0, len(closer))
|
||||
for _, clp := range closer {
|
||||
|
||||
// == to self? thats bad
|
||||
if clp == dht.self {
|
||||
logger.Error("BUG betterPeersToQuery: attempted to return self! this shouldn't happen...")
|
||||
return nil
|
||||
}
|
||||
// Dont send a peer back themselves
|
||||
if clp == from {
|
||||
continue
|
||||
}
|
||||
|
||||
filtered = append(filtered, clp)
|
||||
}
|
||||
|
||||
// ok seems like closer nodes
|
||||
return filtered
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) setMode(m mode) error {
|
||||
dht.modeLk.Lock()
|
||||
defer dht.modeLk.Unlock()
|
||||
|
||||
if m == dht.mode {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch m {
|
||||
case modeServer:
|
||||
return dht.moveToServerMode()
|
||||
case modeClient:
|
||||
return dht.moveToClientMode()
|
||||
default:
|
||||
return fmt.Errorf("unrecognized dht mode: %d", m)
|
||||
}
|
||||
}
|
||||
|
||||
// moveToServerMode advertises (via libp2p identify updates) that we are able to respond to DHT queries and sets the appropriate stream handlers.
|
||||
// Note: We may support responding to queries with protocols aside from our primary ones in order to support
|
||||
// interoperability with older versions of the DHT protocol.
|
||||
func (dht *IpfsDHT) moveToServerMode() error {
|
||||
dht.mode = modeServer
|
||||
for _, p := range dht.serverProtocols {
|
||||
dht.host.SetStreamHandler(p, dht.handleNewStream)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// moveToClientMode stops advertising (and rescinds advertisements via libp2p identify updates) that we are able to
|
||||
// respond to DHT queries and removes the appropriate stream handlers. We also kill all inbound streams that were
|
||||
// utilizing the handled protocols.
|
||||
// Note: We may support responding to queries with protocols aside from our primary ones in order to support
|
||||
// interoperability with older versions of the DHT protocol.
|
||||
func (dht *IpfsDHT) moveToClientMode() error {
|
||||
dht.mode = modeClient
|
||||
for _, p := range dht.serverProtocols {
|
||||
dht.host.RemoveStreamHandler(p)
|
||||
}
|
||||
|
||||
pset := make(map[protocol.ID]bool)
|
||||
for _, p := range dht.serverProtocols {
|
||||
pset[p] = true
|
||||
}
|
||||
|
||||
for _, c := range dht.host.Network().Conns() {
|
||||
for _, s := range c.GetStreams() {
|
||||
if pset[s.Protocol()] {
|
||||
if s.Stat().Direction == network.DirInbound {
|
||||
_ = s.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) getMode() mode {
|
||||
dht.modeLk.Lock()
|
||||
defer dht.modeLk.Unlock()
|
||||
return dht.mode
|
||||
}
|
||||
|
||||
// Context returns the DHT's context.
|
||||
func (dht *IpfsDHT) Context() context.Context {
|
||||
return dht.ctx
|
||||
}
|
||||
|
||||
// RoutingTable returns the DHT's routingTable.
|
||||
func (dht *IpfsDHT) RoutingTable() *kb.RoutingTable {
|
||||
return dht.routingTable
|
||||
}
|
||||
|
||||
// Close calls Process Close.
|
||||
func (dht *IpfsDHT) Close() error {
|
||||
dht.cancel()
|
||||
dht.wg.Wait()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
closes := [...]func() error{
|
||||
dht.rtRefreshManager.Close,
|
||||
dht.providerStore.Close,
|
||||
}
|
||||
var errors [len(closes)]error
|
||||
wg.Add(len(errors))
|
||||
for i, c := range closes {
|
||||
go func(i int, c func() error) {
|
||||
defer wg.Done()
|
||||
errors[i] = c()
|
||||
}(i, c)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return multierr.Combine(errors[:]...)
|
||||
}
|
||||
|
||||
func mkDsKey(s string) ds.Key {
|
||||
return ds.NewKey(base32.RawStdEncoding.EncodeToString([]byte(s)))
|
||||
}
|
||||
|
||||
// PeerID returns the DHT node's Peer ID.
|
||||
func (dht *IpfsDHT) PeerID() peer.ID {
|
||||
return dht.self
|
||||
}
|
||||
|
||||
// PeerKey returns a DHT key, converted from the DHT node's Peer ID.
|
||||
func (dht *IpfsDHT) PeerKey() []byte {
|
||||
return kb.ConvertPeerID(dht.self)
|
||||
}
|
||||
|
||||
// Host returns the libp2p host this DHT is operating with.
|
||||
func (dht *IpfsDHT) Host() host.Host {
|
||||
return dht.host
|
||||
}
|
||||
|
||||
// Ping sends a ping message to the passed peer and waits for a response.
|
||||
func (dht *IpfsDHT) Ping(ctx context.Context, p peer.ID) error {
|
||||
ctx, span := internal.StartSpan(ctx, "IpfsDHT.Ping", trace.WithAttributes(attribute.Stringer("PeerID", p)))
|
||||
defer span.End()
|
||||
return dht.protoMessenger.Ping(ctx, p)
|
||||
}
|
||||
|
||||
// NetworkSize returns the most recent estimation of the DHT network size.
|
||||
// EXPERIMENTAL: We do not provide any guarantees that this method will
|
||||
// continue to exist in the codebase. Use it at your own risk.
|
||||
func (dht *IpfsDHT) NetworkSize() (int32, error) {
|
||||
return dht.nsEstimator.NetworkSize()
|
||||
}
|
||||
|
||||
// newContextWithLocalTags returns a new context.Context with the InstanceID and
|
||||
// PeerID keys populated. It will also take any extra tags that need adding to
|
||||
// the context as tag.Mutators.
|
||||
func (dht *IpfsDHT) newContextWithLocalTags(ctx context.Context, extraTags ...tag.Mutator) context.Context {
|
||||
extraTags = append(
|
||||
extraTags,
|
||||
tag.Upsert(metrics.KeyPeerID, dht.self.String()),
|
||||
tag.Upsert(metrics.KeyInstanceID, fmt.Sprintf("%p", dht)),
|
||||
)
|
||||
ctx, _ = tag.New(
|
||||
ctx,
|
||||
extraTags...,
|
||||
) // ignoring error as it is unrelated to the actual function of this code.
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) maybeAddAddrs(p peer.ID, addrs []ma.Multiaddr, ttl time.Duration) {
|
||||
// Don't add addresses for self or our connected peers. We have better ones.
|
||||
if p == dht.self || dht.host.Network().Connectedness(p) == network.Connected {
|
||||
return
|
||||
}
|
||||
dht.peerstore.AddAddrs(p, dht.filterAddrs(addrs), ttl)
|
||||
}
|
||||
|
||||
func (dht *IpfsDHT) filterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
if f := dht.addrFilter; f != nil {
|
||||
return f(addrs)
|
||||
}
|
||||
return addrs
|
||||
}
|
84
go-libp2p-kad-dht/dht_bootstrap.go
Normal file
84
go-libp2p-kad-dht/dht_bootstrap.go
Normal file
@ -0,0 +1,84 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// DefaultBootstrapPeers is a set of public DHT bootstrap peers provided by libp2p.
|
||||
var DefaultBootstrapPeers []multiaddr.Multiaddr
|
||||
|
||||
// Minimum number of peers in the routing table. If we drop below this and we
|
||||
// see a new peer, we trigger a bootstrap round.
|
||||
var minRTRefreshThreshold = 10
|
||||
|
||||
const (
|
||||
periodicBootstrapInterval = 2 * time.Minute
|
||||
maxNBoostrappers = 2
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, s := range []string{
|
||||
"/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
|
||||
"/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
|
||||
"/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
|
||||
"/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
|
||||
"/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io
|
||||
} {
|
||||
ma, err := multiaddr.NewMultiaddr(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
DefaultBootstrapPeers = append(DefaultBootstrapPeers, ma)
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefaultBootstrapPeerAddrInfos returns the peer.AddrInfos for the default
|
||||
// bootstrap peers so we can use these for initializing the DHT by passing these to the
|
||||
// BootstrapPeers(...) option.
|
||||
func GetDefaultBootstrapPeerAddrInfos() []peer.AddrInfo {
|
||||
ds := make([]peer.AddrInfo, 0, len(DefaultBootstrapPeers))
|
||||
|
||||
for i := range DefaultBootstrapPeers {
|
||||
info, err := peer.AddrInfoFromP2pAddr(DefaultBootstrapPeers[i])
|
||||
if err != nil {
|
||||
logger.Errorw("failed to convert bootstrapper address to peer addr info", "address",
|
||||
DefaultBootstrapPeers[i].String(), err, "err")
|
||||
continue
|
||||
}
|
||||
ds = append(ds, *info)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
// Bootstrap tells the DHT to get into a bootstrapped state satisfying the
|
||||
// IpfsRouter interface.
|
||||
func (dht *IpfsDHT) Bootstrap(ctx context.Context) (err error) {
|
||||
_, end := tracer.Bootstrap(dhtName, ctx)
|
||||
defer func() { end(err) }()
|
||||
|
||||
dht.fixRTIfNeeded()
|
||||
dht.rtRefreshManager.RefreshNoWait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RefreshRoutingTable tells the DHT to refresh it's routing tables.
|
||||
//
|
||||
// The returned channel will block until the refresh finishes, then yield the
|
||||
// error and close. The channel is buffered and safe to ignore.
|
||||
func (dht *IpfsDHT) RefreshRoutingTable() <-chan error {
|
||||
return dht.rtRefreshManager.Refresh(false)
|
||||
}
|
||||
|
||||
// ForceRefresh acts like RefreshRoutingTable but forces the DHT to refresh all
|
||||
// buckets in the Routing Table irrespective of when they were last refreshed.
|
||||
//
|
||||
// The returned channel will block until the refresh finishes, then yield the
|
||||
// error and close. The channel is buffered and safe to ignore.
|
||||
func (dht *IpfsDHT) ForceRefresh() <-chan error {
|
||||
return dht.rtRefreshManager.Refresh(true)
|
||||
}
|
201
go-libp2p-kad-dht/dht_bootstrap_test.go
Normal file
201
go-libp2p-kad-dht/dht_bootstrap_test.go
Normal file
@ -0,0 +1,201 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
kb "github.com/libp2p/go-libp2p-kbucket"
|
||||
"github.com/libp2p/go-libp2p/core/event"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSelfWalkOnAddressChange(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
// create three DHT instances with auto refresh disabled
|
||||
d1 := setupDHT(ctx, t, false, DisableAutoRefresh(), forceAddressUpdateProcessing(t))
|
||||
d2 := setupDHT(ctx, t, false, DisableAutoRefresh())
|
||||
d3 := setupDHT(ctx, t, false, DisableAutoRefresh())
|
||||
|
||||
var connectedTo *IpfsDHT
|
||||
// connect d1 to whoever is "further"
|
||||
if kb.CommonPrefixLen(kb.ConvertPeerID(d1.self), kb.ConvertPeerID(d2.self)) <=
|
||||
kb.CommonPrefixLen(kb.ConvertPeerID(d1.self), kb.ConvertPeerID(d3.self)) {
|
||||
connect(t, ctx, d1, d3)
|
||||
connectedTo = d3
|
||||
} else {
|
||||
connect(t, ctx, d1, d2)
|
||||
connectedTo = d2
|
||||
}
|
||||
|
||||
// then connect d2 AND d3
|
||||
connect(t, ctx, d2, d3)
|
||||
|
||||
// d1 should have ONLY 1 peer in it's RT
|
||||
waitForWellFormedTables(t, []*IpfsDHT{d1}, 1, 1, 2*time.Second)
|
||||
require.Equal(t, connectedTo.self, d1.routingTable.ListPeers()[0])
|
||||
|
||||
// now emit the address change event
|
||||
em, err := d1.host.EventBus().Emitter(&event.EvtLocalAddressesUpdated{})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, em.Emit(event.EvtLocalAddressesUpdated{}))
|
||||
waitForWellFormedTables(t, []*IpfsDHT{d1}, 2, 2, 2*time.Second)
|
||||
// it should now have both peers in the RT
|
||||
ps := d1.routingTable.ListPeers()
|
||||
require.Contains(t, ps, d2.self)
|
||||
require.Contains(t, ps, d3.self)
|
||||
}
|
||||
|
||||
func TestDefaultBootstrappers(t *testing.T) {
|
||||
ds := GetDefaultBootstrapPeerAddrInfos()
|
||||
require.NotEmpty(t, ds)
|
||||
require.Len(t, ds, len(DefaultBootstrapPeers))
|
||||
|
||||
dfmap := make(map[peer.ID]peer.AddrInfo)
|
||||
for _, p := range DefaultBootstrapPeers {
|
||||
info, err := peer.AddrInfoFromP2pAddr(p)
|
||||
require.NoError(t, err)
|
||||
dfmap[info.ID] = *info
|
||||
}
|
||||
|
||||
for _, p := range ds {
|
||||
inf, ok := dfmap[p.ID]
|
||||
require.True(t, ok)
|
||||
require.ElementsMatch(t, p.Addrs, inf.Addrs)
|
||||
delete(dfmap, p.ID)
|
||||
}
|
||||
require.Empty(t, dfmap)
|
||||
}
|
||||
|
||||
func TestBootstrappersReplacable(t *testing.T) {
|
||||
old := rtFreezeTimeout
|
||||
rtFreezeTimeout = 100 * time.Millisecond
|
||||
defer func() {
|
||||
rtFreezeTimeout = old
|
||||
}()
|
||||
ctx := context.Background()
|
||||
d := setupDHT(ctx, t, false, disableFixLowPeersRoutine(t), BucketSize(2))
|
||||
defer d.host.Close()
|
||||
defer d.Close()
|
||||
|
||||
var d1 *IpfsDHT
|
||||
var d2 *IpfsDHT
|
||||
|
||||
// d1 & d2 have a cpl of 0
|
||||
for {
|
||||
d1 = setupDHT(ctx, t, false, disableFixLowPeersRoutine(t))
|
||||
if kb.CommonPrefixLen(d.selfKey, d1.selfKey) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
d2 = setupDHT(ctx, t, false, disableFixLowPeersRoutine(t))
|
||||
if kb.CommonPrefixLen(d.selfKey, d2.selfKey) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer d1.host.Close()
|
||||
defer d1.Close()
|
||||
|
||||
defer d2.host.Close()
|
||||
defer d2.Close()
|
||||
|
||||
connect(t, ctx, d, d1)
|
||||
connect(t, ctx, d, d2)
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
|
||||
// d3 & d4 with cpl=0 will go in as d1 & d2 are replacable.
|
||||
var d3 *IpfsDHT
|
||||
var d4 *IpfsDHT
|
||||
|
||||
for {
|
||||
d3 = setupDHT(ctx, t, false, disableFixLowPeersRoutine(t))
|
||||
if kb.CommonPrefixLen(d.selfKey, d3.selfKey) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
d4 = setupDHT(ctx, t, false, disableFixLowPeersRoutine(t))
|
||||
if kb.CommonPrefixLen(d.selfKey, d4.selfKey) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
defer d3.host.Close()
|
||||
defer d3.Close()
|
||||
defer d4.host.Close()
|
||||
defer d4.Close()
|
||||
|
||||
connect(t, ctx, d, d3)
|
||||
connect(t, ctx, d, d4)
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d3.self)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d4.self)
|
||||
|
||||
// do couple of refreshes and wait for the Routing Table to be "frozen".
|
||||
<-d.RefreshRoutingTable()
|
||||
<-d.RefreshRoutingTable()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// adding d5 fails because RT is frozen
|
||||
var d5 *IpfsDHT
|
||||
for {
|
||||
d5 = setupDHT(ctx, t, false, disableFixLowPeersRoutine(t))
|
||||
if kb.CommonPrefixLen(d.selfKey, d5.selfKey) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer d5.host.Close()
|
||||
defer d5.Close()
|
||||
|
||||
connectNoSync(t, ctx, d, d5)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d3.self)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d4.self)
|
||||
|
||||
// Let's empty the routing table
|
||||
for _, p := range d.routingTable.ListPeers() {
|
||||
d.routingTable.RemovePeer(p)
|
||||
}
|
||||
require.Len(t, d.routingTable.ListPeers(), 0)
|
||||
|
||||
// adding d1 & d2 works now because there is space in the Routing Table
|
||||
require.NoError(t, d.host.Network().ClosePeer(d1.self))
|
||||
require.NoError(t, d.host.Network().ClosePeer(d2.self))
|
||||
connect(t, ctx, d, d1)
|
||||
connect(t, ctx, d, d2)
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d1.self)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d2.self)
|
||||
|
||||
// adding d3 & d4 also works because the RT is not frozen.
|
||||
require.NoError(t, d.host.Network().ClosePeer(d3.self))
|
||||
require.NoError(t, d.host.Network().ClosePeer(d4.self))
|
||||
connect(t, ctx, d, d3)
|
||||
connect(t, ctx, d, d4)
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d3.self)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d4.self)
|
||||
|
||||
// run refreshes and freeze the RT
|
||||
<-d.RefreshRoutingTable()
|
||||
<-d.RefreshRoutingTable()
|
||||
time.Sleep(1 * time.Second)
|
||||
// cant add d1 & d5 because RT is frozen.
|
||||
require.NoError(t, d.host.Network().ClosePeer(d1.self))
|
||||
require.NoError(t, d.host.Network().ClosePeer(d5.self))
|
||||
connectNoSync(t, ctx, d, d1)
|
||||
connectNoSync(t, ctx, d, d5)
|
||||
d.peerFound(d5.self)
|
||||
d.peerFound(d1.self)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
require.Len(t, d.routingTable.ListPeers(), 2)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d3.self)
|
||||
require.Contains(t, d.routingTable.ListPeers(), d4.self)
|
||||
}
|
243
go-libp2p-kad-dht/dht_filters.go
Normal file
243
go-libp2p-kad-dht/dht_filters.go
Normal file
@ -0,0 +1,243 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
|
||||
"github.com/google/gopacket/routing"
|
||||
netroute "github.com/libp2p/go-netroute"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
|
||||
dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config"
|
||||
)
|
||||
|
||||
// QueryFilterFunc is a filter applied when considering peers to dial when querying
|
||||
type QueryFilterFunc = dhtcfg.QueryFilterFunc
|
||||
|
||||
// RouteTableFilterFunc is a filter applied when considering connections to keep in
|
||||
// the local route table.
|
||||
type RouteTableFilterFunc = dhtcfg.RouteTableFilterFunc
|
||||
|
||||
var publicCIDR6 = "2000::/3"
|
||||
var public6 *net.IPNet
|
||||
|
||||
func init() {
|
||||
_, public6, _ = net.ParseCIDR(publicCIDR6)
|
||||
}
|
||||
|
||||
// isPublicAddr follows the logic of manet.IsPublicAddr, except it uses
|
||||
// a stricter definition of "public" for ipv6: namely "is it in 2000::/3"?
|
||||
func isPublicAddr(a ma.Multiaddr) bool {
|
||||
ip, err := manet.ToIP(a)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
return !inAddrRange(ip, manet.Private4) && !inAddrRange(ip, manet.Unroutable4)
|
||||
}
|
||||
|
||||
return public6.Contains(ip)
|
||||
}
|
||||
|
||||
// isPrivateAddr follows the logic of manet.IsPrivateAddr, except that
|
||||
// it uses a stricter definition of "public" for ipv6
|
||||
func isPrivateAddr(a ma.Multiaddr) bool {
|
||||
ip, err := manet.ToIP(a)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
return inAddrRange(ip, manet.Private4)
|
||||
}
|
||||
|
||||
return !public6.Contains(ip) && !inAddrRange(ip, manet.Unroutable6)
|
||||
}
|
||||
|
||||
// PublicQueryFilter returns true if the peer is suspected of being publicly accessible
|
||||
func PublicQueryFilter(_ interface{}, ai peer.AddrInfo) bool {
|
||||
if len(ai.Addrs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var hasPublicAddr bool
|
||||
for _, a := range ai.Addrs {
|
||||
if !isRelayAddr(a) && isPublicAddr(a) {
|
||||
hasPublicAddr = true
|
||||
}
|
||||
}
|
||||
return hasPublicAddr
|
||||
}
|
||||
|
||||
type hasHost interface {
|
||||
Host() host.Host
|
||||
}
|
||||
|
||||
var _ QueryFilterFunc = PublicQueryFilter
|
||||
|
||||
// PublicRoutingTableFilter allows a peer to be added to the routing table if the connections to that peer indicate
|
||||
// that it is on a public network
|
||||
func PublicRoutingTableFilter(dht interface{}, p peer.ID) bool {
|
||||
d := dht.(hasHost)
|
||||
|
||||
conns := d.Host().Network().ConnsToPeer(p)
|
||||
if len(conns) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Do we have a public address for this peer?
|
||||
id := conns[0].RemotePeer()
|
||||
known := d.Host().Peerstore().PeerInfo(id)
|
||||
for _, a := range known.Addrs {
|
||||
if !isRelayAddr(a) && isPublicAddr(a) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var _ RouteTableFilterFunc = PublicRoutingTableFilter
|
||||
|
||||
// PrivateQueryFilter doens't currently restrict which peers we are willing to query from the local DHT.
|
||||
func PrivateQueryFilter(_ interface{}, ai peer.AddrInfo) bool {
|
||||
return len(ai.Addrs) > 0
|
||||
}
|
||||
|
||||
var _ QueryFilterFunc = PrivateQueryFilter
|
||||
|
||||
// We call this very frequently but routes can technically change at runtime.
|
||||
// Cache it for two minutes.
|
||||
const routerCacheTime = 2 * time.Minute
|
||||
|
||||
var routerCache struct {
|
||||
sync.RWMutex
|
||||
router routing.Router
|
||||
expires time.Time
|
||||
}
|
||||
|
||||
func getCachedRouter() routing.Router {
|
||||
routerCache.RLock()
|
||||
router := routerCache.router
|
||||
expires := routerCache.expires
|
||||
routerCache.RUnlock()
|
||||
|
||||
if time.Now().Before(expires) {
|
||||
return router
|
||||
}
|
||||
|
||||
routerCache.Lock()
|
||||
defer routerCache.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
if now.Before(routerCache.expires) {
|
||||
return router
|
||||
}
|
||||
routerCache.router, _ = netroute.New()
|
||||
routerCache.expires = now.Add(routerCacheTime)
|
||||
return router
|
||||
}
|
||||
|
||||
// PrivateRoutingTableFilter allows a peer to be added to the routing table if the connections to that peer indicate
|
||||
// that it is on a private network
|
||||
func PrivateRoutingTableFilter(dht interface{}, p peer.ID) bool {
|
||||
d := dht.(hasHost)
|
||||
conns := d.Host().Network().ConnsToPeer(p)
|
||||
return privRTFilter(d, conns)
|
||||
}
|
||||
|
||||
func privRTFilter(dht interface{}, conns []network.Conn) bool {
|
||||
d := dht.(hasHost)
|
||||
h := d.Host()
|
||||
|
||||
router := getCachedRouter()
|
||||
myAdvertisedIPs := make([]net.IP, 0)
|
||||
for _, a := range h.Addrs() {
|
||||
if isPublicAddr(a) && !isRelayAddr(a) {
|
||||
ip, err := manet.ToIP(a)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
myAdvertisedIPs = append(myAdvertisedIPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range conns {
|
||||
ra := c.RemoteMultiaddr()
|
||||
if isPrivateAddr(ra) && !isRelayAddr(ra) {
|
||||
return true
|
||||
}
|
||||
|
||||
if isPublicAddr(ra) {
|
||||
ip, err := manet.ToIP(ra)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// if the ip is the same as one of the local host's public advertised IPs - then consider it local
|
||||
for _, i := range myAdvertisedIPs {
|
||||
if i.Equal(ip) {
|
||||
return true
|
||||
}
|
||||
if ip.To4() == nil {
|
||||
if i.To4() == nil && isEUI(ip) && sameV6Net(i, ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there's no gateway - a direct host in the OS routing table - then consider it local
|
||||
// This is relevant in particular to ipv6 networks where the addresses may all be public,
|
||||
// but the nodes are aware of direct links between each other.
|
||||
if router != nil {
|
||||
_, gw, _, err := router.Route(ip)
|
||||
if gw == nil && err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var _ RouteTableFilterFunc = PrivateRoutingTableFilter
|
||||
|
||||
func isEUI(ip net.IP) bool {
|
||||
// per rfc 2373
|
||||
return len(ip) == net.IPv6len && ip[11] == 0xff && ip[12] == 0xfe
|
||||
}
|
||||
|
||||
func sameV6Net(a, b net.IP) bool {
|
||||
//lint:ignore SA1021 We're comparing only parts of the IP address here.
|
||||
return len(a) == net.IPv6len && len(b) == net.IPv6len && bytes.Equal(a[0:8], b[0:8]) //nolint
|
||||
}
|
||||
|
||||
func isRelayAddr(a ma.Multiaddr) bool {
|
||||
found := false
|
||||
ma.ForEach(a, func(c ma.Component, e error) bool {
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
found = c.Protocol().Code == ma.P_CIRCUIT
|
||||
return !found
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
func inAddrRange(ip net.IP, ipnets []*net.IPNet) bool {
|
||||
for _, ipnet := range ipnets {
|
||||
if ipnet.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
79
go-libp2p-kad-dht/dht_filters_test.go
Normal file
79
go-libp2p-kad-dht/dht_filters_test.go
Normal file
@ -0,0 +1,79 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
)
|
||||
|
||||
func TestIsRelay(t *testing.T) {
|
||||
a, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5002/p2p/QmdPU7PfRyKehdrP5A3WqmjyD6bhVpU1mLGKppa2FjGDjZ/p2p-circuit/p2p/QmVT6GYwjeeAF5TR485Yc58S3xRF5EFsZ5YAF4VcP3URHt")
|
||||
if !isRelayAddr(a) {
|
||||
t.Fatalf("thought %s was not a relay", a)
|
||||
}
|
||||
a, _ = ma.NewMultiaddr("/p2p-circuit/p2p/QmVT6GYwjeeAF5TR485Yc58S3xRF5EFsZ5YAF4VcP3URHt")
|
||||
if !isRelayAddr(a) {
|
||||
t.Fatalf("thought %s was not a relay", a)
|
||||
}
|
||||
a, _ = ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5002/p2p/QmdPU7PfRyKehdrP5A3WqmjyD6bhVpU1mLGKppa2FjGDjZ")
|
||||
if isRelayAddr(a) {
|
||||
t.Fatalf("thought %s was a relay", a)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type mockConn struct {
|
||||
local peer.AddrInfo
|
||||
remote peer.AddrInfo
|
||||
|
||||
isClosed atomic.Bool
|
||||
}
|
||||
|
||||
var _ network.Conn = (*mockConn)(nil)
|
||||
|
||||
func (m *mockConn) ID() string { return "0" }
|
||||
func (m *mockConn) Close() error {
|
||||
m.isClosed.Store(true)
|
||||
return nil
|
||||
}
|
||||
func (m *mockConn) NewStream(context.Context) (network.Stream, error) { return nil, nil }
|
||||
func (m *mockConn) GetStreams() []network.Stream { return []network.Stream{} }
|
||||
func (m *mockConn) Stat() network.ConnStats {
|
||||
return network.ConnStats{Stats: network.Stats{Direction: network.DirOutbound}}
|
||||
}
|
||||
func (m *mockConn) Scope() network.ConnScope { return &network.NullScope{} }
|
||||
func (m *mockConn) LocalMultiaddr() ma.Multiaddr { return m.local.Addrs[0] }
|
||||
func (m *mockConn) RemoteMultiaddr() ma.Multiaddr { return m.remote.Addrs[0] }
|
||||
func (m *mockConn) LocalPeer() peer.ID { return m.local.ID }
|
||||
func (m *mockConn) LocalPrivateKey() ic.PrivKey { return nil }
|
||||
func (m *mockConn) RemotePeer() peer.ID { return m.remote.ID }
|
||||
func (m *mockConn) RemotePublicKey() ic.PubKey { return nil }
|
||||
func (m *mockConn) ConnState() network.ConnectionState { return network.ConnectionState{} }
|
||||
func (m *mockConn) IsClosed() bool { return m.isClosed.Load() }
|
||||
|
||||
func TestFilterCaching(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
d := setupDHT(ctx, t, true)
|
||||
|
||||
remote, _ := manet.FromIP(net.IPv4(8, 8, 8, 8))
|
||||
if privRTFilter(d, []network.Conn{&mockConn{
|
||||
local: d.Host().Peerstore().PeerInfo(d.Host().ID()),
|
||||
remote: peer.AddrInfo{ID: "", Addrs: []ma.Multiaddr{remote}},
|
||||
}}) {
|
||||
t.Fatal("filter should prevent public remote peers.")
|
||||
}
|
||||
|
||||
r1 := getCachedRouter()
|
||||
r2 := getCachedRouter()
|
||||
if r1 != r2 {
|
||||
t.Fatal("router should be returned multiple times.")
|
||||
}
|
||||
}
|
166
go-libp2p-kad-dht/dht_net.go
Normal file
166
go-libp2p-kad-dht/dht_net.go
Normal file
@ -0,0 +1,166 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
|
||||
"github.com/libp2p/go-libp2p-kad-dht/internal/net"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/metrics"
|
||||
pb "github.com/libp2p/go-libp2p-kad-dht/pb"
|
||||
|
||||
"github.com/libp2p/go-msgio"
|
||||
"go.opencensus.io/stats"
|
||||
"go.opencensus.io/tag"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var dhtStreamIdleTimeout = 1 * time.Minute
|
||||
|
||||
// ErrReadTimeout is an error that occurs when no message is read within the timeout period.
|
||||
var ErrReadTimeout = net.ErrReadTimeout
|
||||
|
||||
// handleNewStream implements the network.StreamHandler
|
||||
func (dht *IpfsDHT) handleNewStream(s network.Stream) {
|
||||
if dht.handleNewMessage(s) {
|
||||
// If we exited without error, close gracefully.
|
||||
_ = s.Close()
|
||||
} else {
|
||||
// otherwise, send an error.
|
||||
_ = s.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true on orderly completion of writes (so we can Close the stream).
|
||||
func (dht *IpfsDHT) handleNewMessage(s network.Stream) bool {
|
||||
ctx := dht.ctx
|
||||
r := msgio.NewVarintReaderSize(s, network.MessageSizeMax)
|
||||
|
||||
mPeer := s.Conn().RemotePeer()
|
||||
|
||||
timer := time.AfterFunc(dhtStreamIdleTimeout, func() { _ = s.Reset() })
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
if dht.getMode() != modeServer {
|
||||
logger.Debugf("ignoring incoming dht message while not in server mode")
|
||||
return false
|
||||
}
|
||||
|
||||
var req pb.Message
|
||||
msgbytes, err := r.ReadMsg()
|
||||
msgLen := len(msgbytes)
|
||||
if err != nil {
|
||||
r.ReleaseMsg(msgbytes)
|
||||
if err == io.EOF {
|
||||
return true
|
||||
}
|
||||
// This string test is necessary because there isn't a single stream reset error
|
||||
// instance in use.
|
||||
if c := baseLogger.Check(zap.DebugLevel, "error reading message"); c != nil && err.Error() != "stream reset" {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Error(err))
|
||||
}
|
||||
if msgLen > 0 {
|
||||
_ = stats.RecordWithTags(ctx,
|
||||
[]tag.Mutator{tag.Upsert(metrics.KeyMessageType, "UNKNOWN")},
|
||||
metrics.ReceivedMessages.M(1),
|
||||
metrics.ReceivedMessageErrors.M(1),
|
||||
metrics.ReceivedBytes.M(int64(msgLen)),
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
err = req.Unmarshal(msgbytes)
|
||||
r.ReleaseMsg(msgbytes)
|
||||
if err != nil {
|
||||
if c := baseLogger.Check(zap.DebugLevel, "error unmarshaling message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Error(err))
|
||||
}
|
||||
_ = stats.RecordWithTags(ctx,
|
||||
[]tag.Mutator{tag.Upsert(metrics.KeyMessageType, "UNKNOWN")},
|
||||
metrics.ReceivedMessages.M(1),
|
||||
metrics.ReceivedMessageErrors.M(1),
|
||||
metrics.ReceivedBytes.M(int64(msgLen)),
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
timer.Reset(dhtStreamIdleTimeout)
|
||||
|
||||
startTime := time.Now()
|
||||
ctx, _ := tag.New(ctx,
|
||||
tag.Upsert(metrics.KeyMessageType, req.GetType().String()),
|
||||
)
|
||||
|
||||
stats.Record(ctx,
|
||||
metrics.ReceivedMessages.M(1),
|
||||
metrics.ReceivedBytes.M(int64(msgLen)),
|
||||
)
|
||||
|
||||
handler := dht.handlerForMsgType(req.GetType())
|
||||
if handler == nil {
|
||||
stats.Record(ctx, metrics.ReceivedMessageErrors.M(1))
|
||||
if c := baseLogger.Check(zap.DebugLevel, "can't handle received message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if c := baseLogger.Check(zap.DebugLevel, "handling message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())),
|
||||
zap.Binary("key", req.GetKey()))
|
||||
}
|
||||
resp, err := handler(ctx, mPeer, &req)
|
||||
if err != nil {
|
||||
stats.Record(ctx, metrics.ReceivedMessageErrors.M(1))
|
||||
if c := baseLogger.Check(zap.DebugLevel, "error handling message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())),
|
||||
zap.Binary("key", req.GetKey()),
|
||||
zap.Error(err))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if c := baseLogger.Check(zap.DebugLevel, "handled message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())),
|
||||
zap.Binary("key", req.GetKey()),
|
||||
zap.Duration("time", time.Since(startTime)))
|
||||
}
|
||||
|
||||
if resp == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// send out response msg
|
||||
err = net.WriteMsg(s, resp)
|
||||
if err != nil {
|
||||
stats.Record(ctx, metrics.ReceivedMessageErrors.M(1))
|
||||
if c := baseLogger.Check(zap.DebugLevel, "error writing response"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())),
|
||||
zap.Binary("key", req.GetKey()),
|
||||
zap.Error(err))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
elapsedTime := time.Since(startTime)
|
||||
|
||||
if c := baseLogger.Check(zap.DebugLevel, "responded to message"); c != nil {
|
||||
c.Write(zap.String("from", mPeer.String()),
|
||||
zap.Int32("type", int32(req.GetType())),
|
||||
zap.Binary("key", req.GetKey()),
|
||||
zap.Duration("time", elapsedTime))
|
||||
}
|
||||
|
||||
latencyMillis := float64(elapsedTime) / float64(time.Millisecond)
|
||||
stats.Record(ctx, metrics.InboundRequestLatency.M(latencyMillis))
|
||||
}
|
||||
}
|
358
go-libp2p-kad-dht/dht_options.go
Normal file
358
go-libp2p-kad-dht/dht_options.go
Normal file
@ -0,0 +1,358 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dhtcfg "github.com/libp2p/go-libp2p-kad-dht/internal/config"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/providers"
|
||||
"github.com/libp2p/go-libp2p-kbucket/peerdiversity"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
|
||||
ds "github.com/ipfs/go-datastore"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
)
|
||||
|
||||
// ModeOpt describes what mode the dht should operate in
|
||||
type ModeOpt = dhtcfg.ModeOpt
|
||||
|
||||
const (
|
||||
// ModeAuto utilizes EvtLocalReachabilityChanged events sent over the event bus to dynamically switch the DHT
|
||||
// between Client and Server modes based on network conditions
|
||||
ModeAuto ModeOpt = iota
|
||||
// ModeClient operates the DHT as a client only, it cannot respond to incoming queries
|
||||
ModeClient
|
||||
// ModeServer operates the DHT as a server, it can both send and respond to queries
|
||||
ModeServer
|
||||
// ModeAutoServer operates in the same way as ModeAuto, but acts as a server when reachability is unknown
|
||||
ModeAutoServer
|
||||
)
|
||||
|
||||
// DefaultPrefix is the application specific prefix attached to all DHT protocols by default.
|
||||
const DefaultPrefix protocol.ID = "/ipfs"
|
||||
|
||||
type Option = dhtcfg.Option
|
||||
|
||||
// ProviderStore sets the provider storage manager.
|
||||
func ProviderStore(ps providers.ProviderStore) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.ProviderStore = ps
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingTableLatencyTolerance sets the maximum acceptable latency for peers
|
||||
// in the routing table's cluster.
|
||||
func RoutingTableLatencyTolerance(latency time.Duration) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.LatencyTolerance = latency
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingTableRefreshQueryTimeout sets the timeout for routing table refresh
|
||||
// queries.
|
||||
func RoutingTableRefreshQueryTimeout(timeout time.Duration) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.RefreshQueryTimeout = timeout
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingTableRefreshPeriod sets the period for refreshing buckets in the
|
||||
// routing table. The DHT will refresh buckets every period by:
|
||||
//
|
||||
// 1. First searching for nearby peers to figure out how many buckets we should try to fill.
|
||||
// 1. Then searching for a random key in each bucket that hasn't been queried in
|
||||
// the last refresh period.
|
||||
func RoutingTableRefreshPeriod(period time.Duration) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.RefreshInterval = period
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Datastore configures the DHT to use the specified datastore.
|
||||
//
|
||||
// Defaults to an in-memory (temporary) map.
|
||||
func Datastore(ds ds.Batching) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.Datastore = ds
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Mode configures which mode the DHT operates in (Client, Server, Auto).
|
||||
//
|
||||
// Defaults to ModeAuto.
|
||||
func Mode(m ModeOpt) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.Mode = m
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Validator configures the DHT to use the specified validator.
|
||||
//
|
||||
// Defaults to a namespaced validator that can validate both public key (under the "pk"
|
||||
// namespace) and IPNS records (under the "ipns" namespace). Setting the validator
|
||||
// implies that the user wants to control the validators and therefore the default
|
||||
// public key and IPNS validators will not be added.
|
||||
func Validator(v record.Validator) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.Validator = v
|
||||
c.ValidatorChanged = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NamespacedValidator adds a validator namespaced under `ns`. This option fails
|
||||
// if the DHT is not using a `record.NamespacedValidator` as its validator (it
|
||||
// uses one by default but this can be overridden with the `Validator` option).
|
||||
// Adding a namespaced validator without changing the `Validator` will result in
|
||||
// adding a new validator in addition to the default public key and IPNS validators.
|
||||
// The "pk" and "ipns" namespaces cannot be overridden here unless a new `Validator`
|
||||
// has been set first.
|
||||
//
|
||||
// Example: Given a validator registered as `NamespacedValidator("ipns",
|
||||
// myValidator)`, all records with keys starting with `/ipns/` will be validated
|
||||
// with `myValidator`.
|
||||
func NamespacedValidator(ns string, v record.Validator) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
nsval, ok := c.Validator.(record.NamespacedValidator)
|
||||
if !ok {
|
||||
return fmt.Errorf("can only add namespaced validators to a NamespacedValidator")
|
||||
}
|
||||
nsval[ns] = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProtocolPrefix sets an application specific prefix to be attached to all DHT protocols. For example,
|
||||
// /myapp/kad/1.0.0 instead of /ipfs/kad/1.0.0. Prefix should be of the form /myapp.
|
||||
//
|
||||
// Defaults to dht.DefaultPrefix
|
||||
func ProtocolPrefix(prefix protocol.ID) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.ProtocolPrefix = prefix
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ProtocolExtension adds an application specific protocol to the DHT protocol. For example,
|
||||
// /ipfs/lan/kad/1.0.0 instead of /ipfs/kad/1.0.0. extension should be of the form /lan.
|
||||
func ProtocolExtension(ext protocol.ID) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.ProtocolPrefix += ext
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// V1ProtocolOverride overrides the protocolID used for /kad/1.0.0 with another. This is an
|
||||
// advanced feature, and should only be used to handle legacy networks that have not been
|
||||
// using protocolIDs of the form /app/kad/1.0.0.
|
||||
//
|
||||
// This option will override and ignore the ProtocolPrefix and ProtocolExtension options
|
||||
func V1ProtocolOverride(proto protocol.ID) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.V1ProtocolOverride = proto
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// BucketSize configures the bucket size (k in the Kademlia paper) of the routing table.
|
||||
//
|
||||
// The default value is 20.
|
||||
func BucketSize(bucketSize int) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.BucketSize = bucketSize
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Concurrency configures the number of concurrent requests (alpha in the Kademlia paper) for a given query path.
|
||||
//
|
||||
// The default value is 10.
|
||||
func Concurrency(alpha int) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.Concurrency = alpha
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Resiliency configures the number of peers closest to a target that must have responded in order for a given query
|
||||
// path to complete.
|
||||
//
|
||||
// The default value is 3.
|
||||
func Resiliency(beta int) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.Resiliency = beta
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LookupInterval configures maximal number of go routines that can be used to
|
||||
// perform a lookup check operation, before adding a new node to the routing table.
|
||||
func LookupCheckConcurrency(n int) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.LookupCheckConcurrency = n
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxRecordAge specifies the maximum time that any node will hold onto a record ("PutValue record")
|
||||
// from the time its received. This does not apply to any other forms of validity that
|
||||
// the record may contain.
|
||||
// For example, a record may contain an ipns entry with an EOL saying its valid
|
||||
// until the year 2020 (a great time in the future). For that record to stick around
|
||||
// it must be rebroadcasted more frequently than once every 'MaxRecordAge'
|
||||
func MaxRecordAge(maxAge time.Duration) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.MaxRecordAge = maxAge
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableAutoRefresh completely disables 'auto-refresh' on the DHT routing
|
||||
// table. This means that we will neither refresh the routing table periodically
|
||||
// nor when the routing table size goes below the minimum threshold.
|
||||
func DisableAutoRefresh() Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.AutoRefresh = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableProviders disables storing and retrieving provider records.
|
||||
//
|
||||
// Defaults to enabled.
|
||||
//
|
||||
// WARNING: do not change this unless you're using a forked DHT (i.e., a private
|
||||
// network and/or distinct DHT protocols with the `Protocols` option).
|
||||
func DisableProviders() Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.EnableProviders = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableValues disables storing and retrieving value records (including
|
||||
// public keys).
|
||||
//
|
||||
// Defaults to enabled.
|
||||
//
|
||||
// WARNING: do not change this unless you're using a forked DHT (i.e., a private
|
||||
// network and/or distinct DHT protocols with the `Protocols` option).
|
||||
func DisableValues() Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.EnableValues = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// QueryFilter sets a function that approves which peers may be dialed in a query
|
||||
func QueryFilter(filter QueryFilterFunc) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.QueryPeerFilter = filter
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingTableFilter sets a function that approves which peers may be added to the routing table. The host should
|
||||
// already have at least one connection to the peer under consideration.
|
||||
func RoutingTableFilter(filter RouteTableFilterFunc) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.PeerFilter = filter
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// BootstrapPeers configures the bootstrapping nodes that we will connect to to seed
|
||||
// and refresh our Routing Table if it becomes empty.
|
||||
func BootstrapPeers(bootstrappers ...peer.AddrInfo) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.BootstrapPeers = func() []peer.AddrInfo {
|
||||
return bootstrappers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// BootstrapPeersFunc configures the function that returns the bootstrapping nodes that we will
|
||||
// connect to to seed and refresh our Routing Table if it becomes empty.
|
||||
func BootstrapPeersFunc(getBootstrapPeers func() []peer.AddrInfo) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.BootstrapPeers = getBootstrapPeers
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// RoutingTablePeerDiversityFilter configures the implementation of the `PeerIPGroupFilter` that will be used
|
||||
// to construct the diversity filter for the Routing Table.
|
||||
// Please see the docs for `peerdiversity.PeerIPGroupFilter` AND `peerdiversity.Filter` for more details.
|
||||
func RoutingTablePeerDiversityFilter(pg peerdiversity.PeerIPGroupFilter) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.RoutingTable.DiversityFilter = pg
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// disableFixLowPeersRoutine disables the "fixLowPeers" routine in the DHT.
|
||||
// This is ONLY for tests.
|
||||
func disableFixLowPeersRoutine(t *testing.T) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.DisableFixLowPeers = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// forceAddressUpdateProcessing forces the DHT to handle changes to the hosts addresses.
|
||||
// This occurs even when AutoRefresh has been disabled.
|
||||
// This is ONLY for tests.
|
||||
func forceAddressUpdateProcessing(t *testing.T) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.TestAddressUpdateProcessing = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableOptimisticProvide enables an optimization that skips the last hops of the provide process.
|
||||
// This works by using the network size estimator (which uses the keyspace density of queries)
|
||||
// to optimistically send ADD_PROVIDER requests when we most likely have found the last hop.
|
||||
// It will also run some ADD_PROVIDER requests asynchronously in the background after returning,
|
||||
// this allows to optimistically return earlier if some threshold number of RPCs have succeeded.
|
||||
// The number of background/in-flight queries can be configured with the OptimisticProvideJobsPoolSize
|
||||
// option.
|
||||
//
|
||||
// EXPERIMENTAL: This is an experimental option and might be removed in the future. Use at your own risk.
|
||||
func EnableOptimisticProvide() Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.EnableOptimisticProvide = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OptimisticProvideJobsPoolSize allows to configure the asynchronicity limit for in-flight ADD_PROVIDER RPCs.
|
||||
// It makes sense to set it to a multiple of optProvReturnRatio * BucketSize. Check the description of
|
||||
// EnableOptimisticProvide for more details.
|
||||
//
|
||||
// EXPERIMENTAL: This is an experimental option and might be removed in the future. Use at your own risk.
|
||||
func OptimisticProvideJobsPoolSize(size int) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.OptimisticProvideJobsPoolSize = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddressFilter allows to configure the address filtering function.
|
||||
// This function is run before addresses are added to the peerstore.
|
||||
// It is most useful to avoid adding localhost / local addresses.
|
||||
func AddressFilter(f func([]ma.Multiaddr) []ma.Multiaddr) Option {
|
||||
return func(c *dhtcfg.Config) error {
|
||||
c.AddressFilter = f
|
||||
return nil
|
||||
}
|
||||
}
|
2478
go-libp2p-kad-dht/dht_test.go
Normal file
2478
go-libp2p-kad-dht/dht_test.go
Normal file
File diff suppressed because it is too large
Load Diff
3
go-libp2p-kad-dht/doc.go
Normal file
3
go-libp2p-kad-dht/doc.go
Normal file
@ -0,0 +1,3 @@
|
||||
// Package dht implements a distributed hash table that satisfies the ipfs routing
|
||||
// interface. This DHT is modeled after kademlia with S/Kademlia modifications.
|
||||
package dht
|
394
go-libp2p-kad-dht/dual/dual.go
Normal file
394
go-libp2p-kad-dht/dual/dual.go
Normal file
@ -0,0 +1,394 @@
|
||||
// Package dual provides an implementation of a split or "dual" dht, where two parallel instances
|
||||
// are maintained for the global internet and the local LAN respectively.
|
||||
package dual
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
"github.com/libp2p/go-libp2p-kad-dht/internal"
|
||||
"github.com/libp2p/go-libp2p-routing-helpers/tracing"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
kb "github.com/libp2p/go-libp2p-kbucket"
|
||||
"github.com/libp2p/go-libp2p-kbucket/peerdiversity"
|
||||
helper "github.com/libp2p/go-libp2p-routing-helpers"
|
||||
ci "github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
"github.com/libp2p/go-libp2p/core/protocol"
|
||||
"github.com/libp2p/go-libp2p/core/routing"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
const tracer = tracing.Tracer("go-libp2p-kad-dht/dual")
|
||||
const dualName = "Dual"
|
||||
|
||||
// DHT implements the routing interface to provide two concrete DHT implementationts for use
|
||||
// in IPFS that are used to support both global network users and disjoint LAN usecases.
|
||||
type DHT struct {
|
||||
WAN *dht.IpfsDHT
|
||||
LAN *dht.IpfsDHT
|
||||
}
|
||||
|
||||
// LanExtension is used to differentiate local protocol requests from those on the WAN DHT.
|
||||
const LanExtension protocol.ID = "/lan"
|
||||
|
||||
// Assert that IPFS assumptions about interfaces aren't broken. These aren't a
|
||||
// guarantee, but we can use them to aid refactoring.
|
||||
var (
|
||||
_ routing.ContentRouting = (*DHT)(nil)
|
||||
_ routing.Routing = (*DHT)(nil)
|
||||
_ routing.PeerRouting = (*DHT)(nil)
|
||||
_ routing.PubKeyFetcher = (*DHT)(nil)
|
||||
_ routing.ValueStore = (*DHT)(nil)
|
||||
)
|
||||
|
||||
var (
|
||||
maxPrefixCountPerCpl = 2
|
||||
maxPrefixCount = 3
|
||||
)
|
||||
|
||||
type config struct {
|
||||
wan, lan []dht.Option
|
||||
}
|
||||
|
||||
func (cfg *config) apply(opts ...Option) error {
|
||||
for i, o := range opts {
|
||||
if err := o(cfg); err != nil {
|
||||
return fmt.Errorf("dual dht option %d failed: %w", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Option is an option used to configure the Dual DHT.
|
||||
type Option func(*config) error
|
||||
|
||||
// WanDHTOption applies the given DHT options to the WAN DHT.
|
||||
func WanDHTOption(opts ...dht.Option) Option {
|
||||
return func(c *config) error {
|
||||
c.wan = append(c.wan, opts...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LanDHTOption applies the given DHT options to the LAN DHT.
|
||||
func LanDHTOption(opts ...dht.Option) Option {
|
||||
return func(c *config) error {
|
||||
c.lan = append(c.lan, opts...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DHTOption applies the given DHT options to both the WAN and the LAN DHTs.
|
||||
func DHTOption(opts ...dht.Option) Option {
|
||||
return func(c *config) error {
|
||||
c.lan = append(c.lan, opts...)
|
||||
c.wan = append(c.wan, opts...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new DualDHT instance. Options provided are forwarded on to the two concrete
|
||||
// IpfsDHT internal constructions, modulo additional options used by the Dual DHT to enforce
|
||||
// the LAN-vs-WAN distinction.
|
||||
// Note: query or routing table functional options provided as arguments to this function
|
||||
// will be overriden by this constructor.
|
||||
func New(ctx context.Context, h host.Host, options ...Option) (*DHT, error) {
|
||||
var cfg config
|
||||
err := cfg.apply(
|
||||
WanDHTOption(
|
||||
dht.QueryFilter(dht.PublicQueryFilter),
|
||||
dht.RoutingTableFilter(dht.PublicRoutingTableFilter),
|
||||
dht.RoutingTablePeerDiversityFilter(dht.NewRTPeerDiversityFilter(h, maxPrefixCountPerCpl, maxPrefixCount)),
|
||||
// filter out all private addresses
|
||||
dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { is, err := manet.IsPublicAddr(a); return is && err == nil })
|
||||
}),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cfg.apply(
|
||||
LanDHTOption(
|
||||
dht.ProtocolExtension(LanExtension),
|
||||
dht.QueryFilter(dht.PrivateQueryFilter),
|
||||
dht.RoutingTableFilter(dht.PrivateRoutingTableFilter),
|
||||
// filter out localhost IP addresses
|
||||
dht.AddressFilter(func(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||
return ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { return !manet.IsIPLoopback(a) })
|
||||
}),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = cfg.apply(options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wan, err := dht.New(ctx, h, cfg.wan...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Unless overridden by user supplied options, the LAN DHT should default
|
||||
// to 'AutoServer' mode.
|
||||
if wan.Mode() != dht.ModeClient {
|
||||
cfg.lan = append(cfg.lan, dht.Mode(dht.ModeServer))
|
||||
}
|
||||
lan, err := dht.New(ctx, h, cfg.lan...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
impl := DHT{wan, lan}
|
||||
return &impl, nil
|
||||
}
|
||||
|
||||
// Close closes the DHT context.
|
||||
func (dht *DHT) Close() error {
|
||||
return combineErrors(dht.WAN.Close(), dht.LAN.Close())
|
||||
}
|
||||
|
||||
// WANActive returns true when the WAN DHT is active (has peers).
|
||||
func (dht *DHT) WANActive() bool {
|
||||
return dht.WAN.RoutingTable().Size() > 0
|
||||
}
|
||||
|
||||
// Provide adds the given cid to the content routing system.
|
||||
func (dht *DHT) Provide(ctx context.Context, key cid.Cid, announce bool) (err error) {
|
||||
ctx, end := tracer.Provide(dualName, ctx, key, announce)
|
||||
defer func() { end(err) }()
|
||||
|
||||
if dht.WANActive() {
|
||||
return dht.WAN.Provide(ctx, key, announce)
|
||||
}
|
||||
return dht.LAN.Provide(ctx, key, announce)
|
||||
}
|
||||
|
||||
// GetRoutingTableDiversityStats fetches the Routing Table Diversity Stats.
|
||||
func (dht *DHT) GetRoutingTableDiversityStats() []peerdiversity.CplDiversityStats {
|
||||
if dht.WANActive() {
|
||||
return dht.WAN.GetRoutingTableDiversityStats()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindProvidersAsync searches for peers who are able to provide a given key
|
||||
func (dht *DHT) FindProvidersAsync(ctx context.Context, key cid.Cid, count int) (ch <-chan peer.AddrInfo) {
|
||||
ctx, end := tracer.FindProvidersAsync(dualName, ctx, key, count)
|
||||
defer func() { ch = end(ch, nil) }()
|
||||
|
||||
reqCtx, cancel := context.WithCancel(ctx)
|
||||
outCh := make(chan peer.AddrInfo)
|
||||
|
||||
// Register for and merge query events if we care about them.
|
||||
subCtx := reqCtx
|
||||
var evtCh <-chan *routing.QueryEvent
|
||||
if routing.SubscribesToQueryEvents(ctx) {
|
||||
subCtx, evtCh = routing.RegisterForQueryEvents(reqCtx)
|
||||
}
|
||||
|
||||
subCtx, span := internal.StartSpan(subCtx, "Dual.worker")
|
||||
wanCh := dht.WAN.FindProvidersAsync(subCtx, key, count)
|
||||
lanCh := dht.LAN.FindProvidersAsync(subCtx, key, count)
|
||||
zeroCount := (count == 0)
|
||||
go func() {
|
||||
defer span.End()
|
||||
|
||||
defer cancel()
|
||||
defer close(outCh)
|
||||
|
||||
found := make(map[peer.ID]struct{}, count)
|
||||
var pi peer.AddrInfo
|
||||
var qEv *routing.QueryEvent
|
||||
for (zeroCount || count > 0) && (wanCh != nil || lanCh != nil) {
|
||||
var ok bool
|
||||
select {
|
||||
case qEv, ok = <-evtCh:
|
||||
if !ok {
|
||||
evtCh = nil
|
||||
} else if qEv != nil && qEv.Type != routing.QueryError {
|
||||
routing.PublishQueryEvent(reqCtx, qEv)
|
||||
}
|
||||
continue
|
||||
case pi, ok = <-wanCh:
|
||||
if !ok {
|
||||
span.AddEvent("wan finished")
|
||||
wanCh = nil
|
||||
continue
|
||||
}
|
||||
case pi, ok = <-lanCh:
|
||||
if !ok {
|
||||
span.AddEvent("lan finished")
|
||||
lanCh = nil
|
||||
continue
|
||||
}
|
||||
}
|
||||
// already found
|
||||
if _, ok = found[pi.ID]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case outCh <- pi:
|
||||
found[pi.ID] = struct{}{}
|
||||
count--
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
if qEv != nil && qEv.Type == routing.QueryError && len(found) == 0 {
|
||||
routing.PublishQueryEvent(reqCtx, qEv)
|
||||
}
|
||||
}()
|
||||
return outCh
|
||||
}
|
||||
|
||||
// FindPeer searches for a peer with given ID
|
||||
// Note: with signed peer records, we can change this to short circuit once either DHT returns.
|
||||
func (dht *DHT) FindPeer(ctx context.Context, pid peer.ID) (pi peer.AddrInfo, err error) {
|
||||
ctx, end := tracer.FindPeer(dualName, ctx, pid)
|
||||
defer func() { end(pi, err) }()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
var wanInfo, lanInfo peer.AddrInfo
|
||||
var wanErr, lanErr error
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wanInfo, wanErr = dht.WAN.FindPeer(ctx, pid)
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
lanInfo, lanErr = dht.LAN.FindPeer(ctx, pid)
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Combine addresses. Try to avoid doing unnecessary work while we're at
|
||||
// it. Note: We're ignoring the errors for now as many of our DHT
|
||||
// commands can return both a result and an error.
|
||||
ai := peer.AddrInfo{ID: pid}
|
||||
if len(wanInfo.Addrs) == 0 {
|
||||
ai.Addrs = lanInfo.Addrs
|
||||
} else if len(lanInfo.Addrs) == 0 {
|
||||
ai.Addrs = wanInfo.Addrs
|
||||
} else {
|
||||
// combine addresses
|
||||
deduped := make(map[string]ma.Multiaddr, len(wanInfo.Addrs)+len(lanInfo.Addrs))
|
||||
for _, addr := range wanInfo.Addrs {
|
||||
deduped[string(addr.Bytes())] = addr
|
||||
}
|
||||
for _, addr := range lanInfo.Addrs {
|
||||
deduped[string(addr.Bytes())] = addr
|
||||
}
|
||||
ai.Addrs = make([]ma.Multiaddr, 0, len(deduped))
|
||||
for _, addr := range deduped {
|
||||
ai.Addrs = append(ai.Addrs, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// If one of the commands succeeded, don't return an error.
|
||||
if wanErr == nil || lanErr == nil {
|
||||
return ai, nil
|
||||
}
|
||||
|
||||
// Otherwise, return what we have _and_ return the error.
|
||||
return ai, combineErrors(wanErr, lanErr)
|
||||
}
|
||||
|
||||
func combineErrors(erra, errb error) error {
|
||||
// if the errors are the same, just return one.
|
||||
if erra == errb {
|
||||
return erra
|
||||
}
|
||||
|
||||
// If one of the errors is a kb lookup failure (no peers in routing
|
||||
// table), return the other.
|
||||
if erra == kb.ErrLookupFailure {
|
||||
return errb
|
||||
} else if errb == kb.ErrLookupFailure {
|
||||
return erra
|
||||
}
|
||||
return multierror.Append(erra, errb).ErrorOrNil()
|
||||
}
|
||||
|
||||
// Bootstrap allows callers to hint to the routing system to get into a
|
||||
// Boostrapped state and remain there.
|
||||
func (dht *DHT) Bootstrap(ctx context.Context) (err error) {
|
||||
ctx, end := tracer.Bootstrap(dualName, ctx)
|
||||
defer func() { end(err) }()
|
||||
|
||||
erra := dht.WAN.Bootstrap(ctx)
|
||||
errb := dht.LAN.Bootstrap(ctx)
|
||||
return combineErrors(erra, errb)
|
||||
}
|
||||
|
||||
// PutValue adds value corresponding to given Key.
|
||||
func (dht *DHT) PutValue(ctx context.Context, key string, val []byte, opts ...routing.Option) (err error) {
|
||||
ctx, end := tracer.PutValue(dualName, ctx, key, val, opts...)
|
||||
defer func() { end(err) }()
|
||||
|
||||
if dht.WANActive() {
|
||||
return dht.WAN.PutValue(ctx, key, val, opts...)
|
||||
}
|
||||
return dht.LAN.PutValue(ctx, key, val, opts...)
|
||||
}
|
||||
|
||||
// GetValue searches for the value corresponding to given Key.
|
||||
func (d *DHT) GetValue(ctx context.Context, key string, opts ...routing.Option) (result []byte, err error) {
|
||||
ctx, end := tracer.GetValue(dualName, ctx, key, opts...)
|
||||
defer func() { end(result, err) }()
|
||||
|
||||
lanCtx, cancelLan := context.WithCancel(ctx)
|
||||
defer cancelLan()
|
||||
|
||||
var (
|
||||
lanVal []byte
|
||||
lanErr error
|
||||
lanWaiter sync.WaitGroup
|
||||
)
|
||||
lanWaiter.Add(1)
|
||||
go func() {
|
||||
defer lanWaiter.Done()
|
||||
lanVal, lanErr = d.LAN.GetValue(lanCtx, key, opts...)
|
||||
}()
|
||||
|
||||
wanVal, wanErr := d.WAN.GetValue(ctx, key, opts...)
|
||||
if wanErr == nil {
|
||||
cancelLan()
|
||||
}
|
||||
lanWaiter.Wait()
|
||||
if wanErr == nil {
|
||||
return wanVal, nil
|
||||
}
|
||||
if lanErr == nil {
|
||||
return lanVal, nil
|
||||
}
|
||||
return nil, combineErrors(wanErr, lanErr)
|
||||
}
|
||||
|
||||
// SearchValue searches for better values from this value
|
||||
func (dht *DHT) SearchValue(ctx context.Context, key string, opts ...routing.Option) (ch <-chan []byte, err error) {
|
||||
ctx, end := tracer.SearchValue(dualName, ctx, key, opts...)
|
||||
defer func() { ch, err = end(ch, err) }()
|
||||
|
||||
p := helper.Parallel{Routers: []routing.Routing{dht.WAN, dht.LAN}, Validator: dht.WAN.Validator}
|
||||
return p.SearchValue(ctx, key, opts...)
|
||||
}
|
||||
|
||||
// GetPublicKey returns the public key for the given peer.
|
||||
func (dht *DHT) GetPublicKey(ctx context.Context, pid peer.ID) (ci.PubKey, error) {
|
||||
p := helper.Parallel{Routers: []routing.Routing{dht.WAN, dht.LAN}, Validator: dht.WAN.Validator}
|
||||
return p.GetPublicKey(ctx, pid)
|
||||
}
|
399
go-libp2p-kad-dht/dual/dual_test.go
Normal file
399
go-libp2p-kad-dht/dual/dual_test.go
Normal file
@ -0,0 +1,399 @@
|
||||
package dual
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
u "github.com/ipfs/boxo/util"
|
||||
"github.com/ipfs/go-cid"
|
||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||
test "github.com/libp2p/go-libp2p-kad-dht/internal/testing"
|
||||
record "github.com/libp2p/go-libp2p-record"
|
||||
"github.com/libp2p/go-libp2p/core/host"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
peerstore "github.com/libp2p/go-libp2p/core/peerstore"
|
||||
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"
|
||||
"github.com/multiformats/go-multiaddr"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var wancid, lancid cid.Cid
|
||||
|
||||
func init() {
|
||||
wancid = cid.NewCidV1(cid.DagCBOR, u.Hash([]byte("wan cid -- value")))
|
||||
lancid = cid.NewCidV1(cid.DagCBOR, u.Hash([]byte("lan cid -- value")))
|
||||
}
|
||||
|
||||
type blankValidator struct{}
|
||||
|
||||
func (blankValidator) Validate(_ string, _ []byte) error { return nil }
|
||||
func (blankValidator) Select(_ string, _ [][]byte) (int, error) { return 0, nil }
|
||||
|
||||
type customRtHelper struct {
|
||||
allow peer.ID
|
||||
}
|
||||
|
||||
func MkFilterForPeer() (func(_ interface{}, p peer.ID) bool, *customRtHelper) {
|
||||
helper := customRtHelper{}
|
||||
|
||||
type hasHost interface {
|
||||
Host() host.Host
|
||||
}
|
||||
|
||||
f := func(dht interface{}, p peer.ID) bool {
|
||||
d := dht.(hasHost)
|
||||
conns := d.Host().Network().ConnsToPeer(p)
|
||||
|
||||
for _, c := range conns {
|
||||
if c.RemotePeer() == helper.allow {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return f, &helper
|
||||
}
|
||||
|
||||
func setupDHTWithFilters(ctx context.Context, t *testing.T, options ...dht.Option) (*DHT, []*customRtHelper) {
|
||||
h, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts))
|
||||
require.NoError(t, err)
|
||||
h.Start()
|
||||
t.Cleanup(func() { h.Close() })
|
||||
|
||||
wanFilter, wanRef := MkFilterForPeer()
|
||||
wanOpts := []dht.Option{
|
||||
dht.NamespacedValidator("v", blankValidator{}),
|
||||
dht.ProtocolPrefix("/test"),
|
||||
dht.DisableAutoRefresh(),
|
||||
dht.RoutingTableFilter(wanFilter),
|
||||
}
|
||||
wan, err := dht.New(ctx, h, wanOpts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
lanFilter, lanRef := MkFilterForPeer()
|
||||
lanOpts := []dht.Option{
|
||||
dht.NamespacedValidator("v", blankValidator{}),
|
||||
dht.ProtocolPrefix("/test"),
|
||||
dht.ProtocolExtension(LanExtension),
|
||||
dht.DisableAutoRefresh(),
|
||||
dht.RoutingTableFilter(lanFilter),
|
||||
dht.Mode(dht.ModeServer),
|
||||
}
|
||||
lan, err := dht.New(ctx, h, lanOpts...)
|
||||
require.NoError(t, err)
|
||||
|
||||
impl := DHT{wan, lan}
|
||||
return &impl, []*customRtHelper{wanRef, lanRef}
|
||||
}
|
||||
|
||||
func setupDHT(ctx context.Context, t *testing.T, options ...dht.Option) *DHT {
|
||||
t.Helper()
|
||||
|
||||
host, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts))
|
||||
require.NoError(t, err)
|
||||
host.Start()
|
||||
t.Cleanup(func() { host.Close() })
|
||||
|
||||
baseOpts := []dht.Option{
|
||||
dht.NamespacedValidator("v", blankValidator{}),
|
||||
dht.ProtocolPrefix("/test"),
|
||||
dht.DisableAutoRefresh(),
|
||||
}
|
||||
|
||||
d, err := New(
|
||||
ctx,
|
||||
host,
|
||||
append([]Option{DHTOption(baseOpts...)}, DHTOption(options...))...,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func connect(ctx context.Context, t *testing.T, a, b *dht.IpfsDHT) {
|
||||
t.Helper()
|
||||
bid := b.PeerID()
|
||||
baddr := b.Host().Peerstore().Addrs(bid)
|
||||
if len(baddr) == 0 {
|
||||
t.Fatal("no addresses for connection.")
|
||||
}
|
||||
a.Host().Peerstore().AddAddrs(bid, baddr, peerstore.TempAddrTTL)
|
||||
if err := a.Host().Connect(ctx, peer.AddrInfo{ID: bid}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wait(ctx, t, a, b)
|
||||
}
|
||||
|
||||
func wait(ctx context.Context, t *testing.T, a, b *dht.IpfsDHT) {
|
||||
t.Helper()
|
||||
for a.RoutingTable().Find(b.PeerID()) == "" {
|
||||
// fmt.Fprintf(os.Stderr, "%v\n", a.RoutingTable().GetPeerInfos())
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal(ctx.Err())
|
||||
case <-time.After(time.Millisecond * 5):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupTier(ctx context.Context, t *testing.T) (*DHT, *dht.IpfsDHT, *dht.IpfsDHT) {
|
||||
t.Helper()
|
||||
baseOpts := []dht.Option{
|
||||
dht.NamespacedValidator("v", blankValidator{}),
|
||||
dht.ProtocolPrefix("/test"),
|
||||
dht.DisableAutoRefresh(),
|
||||
}
|
||||
|
||||
d, hlprs := setupDHTWithFilters(ctx, t)
|
||||
|
||||
whost, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts))
|
||||
require.NoError(t, err)
|
||||
whost.Start()
|
||||
t.Cleanup(func() { whost.Close() })
|
||||
|
||||
wan, err := dht.New(
|
||||
ctx,
|
||||
whost,
|
||||
append(baseOpts, dht.Mode(dht.ModeServer))...,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hlprs[0].allow = wan.PeerID()
|
||||
connect(ctx, t, d.WAN, wan)
|
||||
|
||||
lhost, err := bhost.NewHost(swarmt.GenSwarm(t, swarmt.OptDisableReuseport), new(bhost.HostOpts))
|
||||
require.NoError(t, err)
|
||||
lhost.Start()
|
||||
t.Cleanup(func() { lhost.Close() })
|
||||
|
||||
lan, err := dht.New(
|
||||
ctx,
|
||||
lhost,
|
||||
append(baseOpts, dht.Mode(dht.ModeServer), dht.ProtocolExtension("/lan"))...,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hlprs[1].allow = lan.PeerID()
|
||||
connect(ctx, t, d.LAN, lan)
|
||||
|
||||
return d, wan, lan
|
||||
}
|
||||
|
||||
func TestDualModes(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d := setupDHT(ctx, t)
|
||||
defer d.Close()
|
||||
|
||||
if d.WAN.Mode() != dht.ModeAuto {
|
||||
t.Fatal("wrong default mode for wan")
|
||||
} else if d.LAN.Mode() != dht.ModeServer {
|
||||
t.Fatal("wrong default mode for lan")
|
||||
}
|
||||
|
||||
d2 := setupDHT(ctx, t, dht.Mode(dht.ModeClient))
|
||||
defer d2.Close()
|
||||
if d2.WAN.Mode() != dht.ModeClient ||
|
||||
d2.LAN.Mode() != dht.ModeClient {
|
||||
t.Fatal("wrong client mode operation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindProviderAsync(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d, wan, lan := setupTier(ctx, t)
|
||||
defer d.Close()
|
||||
defer wan.Close()
|
||||
defer lan.Close()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
if err := wan.Provide(ctx, wancid, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := lan.Provide(ctx, lancid, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wpc := d.FindProvidersAsync(ctx, wancid, 1)
|
||||
select {
|
||||
case p := <-wpc:
|
||||
if p.ID != wan.PeerID() {
|
||||
t.Fatal("wrong wan provider")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("find provider timeout.")
|
||||
}
|
||||
|
||||
lpc := d.FindProvidersAsync(ctx, lancid, 1)
|
||||
select {
|
||||
case p := <-lpc:
|
||||
if p.ID != lan.PeerID() {
|
||||
t.Fatal("wrong lan provider")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("find provider timeout.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueGetSet(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d, wan, lan := setupTier(ctx, t)
|
||||
defer d.Close()
|
||||
defer wan.Close()
|
||||
defer lan.Close()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
err := d.PutValue(ctx, "/v/hello", []byte("valid"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
val, err := wan.GetValue(ctx, "/v/hello")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(val) != "valid" {
|
||||
t.Fatal("failed to get expected string.")
|
||||
}
|
||||
|
||||
_, err = lan.GetValue(ctx, "/v/hello")
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchValue(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d, wan, lan := setupTier(ctx, t)
|
||||
defer d.Close()
|
||||
defer wan.Close()
|
||||
defer lan.Close()
|
||||
|
||||
d.WAN.Validator.(record.NamespacedValidator)["v"] = test.TestValidator{}
|
||||
d.LAN.Validator.(record.NamespacedValidator)["v"] = test.TestValidator{}
|
||||
|
||||
_ = wan.PutValue(ctx, "/v/hello", []byte("valid"))
|
||||
|
||||
valCh, err := d.SearchValue(ctx, "/v/hello", dht.Quorum(0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case v := <-valCh:
|
||||
if string(v) != "valid" {
|
||||
t.Errorf("expected 'valid', got '%s'", string(v))
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal(ctx.Err())
|
||||
}
|
||||
|
||||
select {
|
||||
case _, ok := <-valCh:
|
||||
if ok {
|
||||
t.Errorf("chan should close")
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal(ctx.Err())
|
||||
}
|
||||
|
||||
err = lan.PutValue(ctx, "/v/hello", []byte("newer"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
valCh, err = d.SearchValue(ctx, "/v/hello", dht.Quorum(0))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var lastVal []byte
|
||||
for c := range valCh {
|
||||
lastVal = c
|
||||
}
|
||||
if string(lastVal) != "newer" {
|
||||
t.Fatal("incorrect best search value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPublicKey(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d, wan, lan := setupTier(ctx, t)
|
||||
defer d.Close()
|
||||
defer wan.Close()
|
||||
defer lan.Close()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
pk, err := d.GetPublicKey(ctx, wan.PeerID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
id, err := peer.IDFromPublicKey(pk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if id != wan.PeerID() {
|
||||
t.Fatal("incorrect PK")
|
||||
}
|
||||
|
||||
pk, err = d.GetPublicKey(ctx, lan.PeerID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
id, err = peer.IDFromPublicKey(pk)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if id != lan.PeerID() {
|
||||
t.Fatal("incorrect PK")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindPeer(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
d, wan, lan := setupTier(ctx, t)
|
||||
defer d.Close()
|
||||
defer wan.Close()
|
||||
defer lan.Close()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
p, err := d.FindPeer(ctx, lan.PeerID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertUniqueMultiaddrs(t, p.Addrs)
|
||||
p, err = d.FindPeer(ctx, wan.PeerID())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertUniqueMultiaddrs(t, p.Addrs)
|
||||
}
|
||||
|
||||
func assertUniqueMultiaddrs(t *testing.T, addrs []multiaddr.Multiaddr) {
|
||||
set := make(map[string]bool)
|
||||
for _, addr := range addrs {
|
||||
if set[string(addr.Bytes())] {
|
||||
t.Errorf("duplicate address %s", addr)
|
||||
}
|
||||
set[string(addr.Bytes())] = true
|
||||
}
|
||||
}
|
247
go-libp2p-kad-dht/events.go
Normal file
247
go-libp2p-kad-dht/events.go
Normal file
@ -0,0 +1,247 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
kbucket "github.com/libp2p/go-libp2p-kbucket"
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
)
|
||||
|
||||
// KeyKadID contains the Kademlia key in string and binary form.
|
||||
type KeyKadID struct {
|
||||
Key string
|
||||
Kad kbucket.ID
|
||||
}
|
||||
|
||||
// NewKeyKadID creates a KeyKadID from a string Kademlia ID.
|
||||
func NewKeyKadID(k string) *KeyKadID {
|
||||
return &KeyKadID{
|
||||
Key: k,
|
||||
Kad: kbucket.ConvertKey(k),
|
||||
}
|
||||
}
|
||||
|
||||
// PeerKadID contains a libp2p Peer ID and a binary Kademlia ID.
|
||||
type PeerKadID struct {
|
||||
Peer peer.ID
|
||||
Kad kbucket.ID
|
||||
}
|
||||
|
||||
// NewPeerKadID creates a PeerKadID from a libp2p Peer ID.
|
||||
func NewPeerKadID(p peer.ID) *PeerKadID {
|
||||
return &PeerKadID{
|
||||
Peer: p,
|
||||
Kad: kbucket.ConvertPeerID(p),
|
||||
}
|
||||
}
|
||||
|
||||
// NewPeerKadIDSlice creates a slice of PeerKadID from the passed slice of libp2p Peer IDs.
|
||||
func NewPeerKadIDSlice(p []peer.ID) []*PeerKadID {
|
||||
r := make([]*PeerKadID, len(p))
|
||||
for i := range p {
|
||||
r[i] = NewPeerKadID(p[i])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// OptPeerKadID returns a pointer to a PeerKadID or nil if the passed Peer ID is it's default value.
|
||||
func OptPeerKadID(p peer.ID) *PeerKadID {
|
||||
if p == "" {
|
||||
return nil
|
||||
}
|
||||
return NewPeerKadID(p)
|
||||
}
|
||||
|
||||
// NewLookupEvent creates a LookupEvent automatically converting the node
|
||||
// libp2p Peer ID to a PeerKadID and the string Kademlia key to a KeyKadID.
|
||||
func NewLookupEvent(
|
||||
node peer.ID,
|
||||
id uuid.UUID,
|
||||
key string,
|
||||
request *LookupUpdateEvent,
|
||||
response *LookupUpdateEvent,
|
||||
terminate *LookupTerminateEvent,
|
||||
) *LookupEvent {
|
||||
return &LookupEvent{
|
||||
Node: NewPeerKadID(node),
|
||||
ID: id,
|
||||
Key: NewKeyKadID(key),
|
||||
Request: request,
|
||||
Response: response,
|
||||
Terminate: terminate,
|
||||
}
|
||||
}
|
||||
|
||||
// LookupEvent is emitted for every notable event that happens during a DHT lookup.
|
||||
// LookupEvent supports JSON marshalling because all of its fields do, recursively.
|
||||
type LookupEvent struct {
|
||||
// Node is the ID of the node performing the lookup.
|
||||
Node *PeerKadID
|
||||
// ID is a unique identifier for the lookup instance.
|
||||
ID uuid.UUID
|
||||
// Key is the Kademlia key used as a lookup target.
|
||||
Key *KeyKadID
|
||||
// Request, if not nil, describes a state update event, associated with an outgoing query request.
|
||||
Request *LookupUpdateEvent
|
||||
// Response, if not nil, describes a state update event, associated with an outgoing query response.
|
||||
Response *LookupUpdateEvent
|
||||
// Terminate, if not nil, describe a termination event.
|
||||
Terminate *LookupTerminateEvent
|
||||
}
|
||||
|
||||
// NewLookupUpdateEvent creates a new lookup update event, automatically converting the passed peer IDs to peer Kad IDs.
|
||||
func NewLookupUpdateEvent(
|
||||
cause peer.ID,
|
||||
source peer.ID,
|
||||
heard []peer.ID,
|
||||
waiting []peer.ID,
|
||||
queried []peer.ID,
|
||||
unreachable []peer.ID,
|
||||
) *LookupUpdateEvent {
|
||||
return &LookupUpdateEvent{
|
||||
Cause: OptPeerKadID(cause),
|
||||
Source: OptPeerKadID(source),
|
||||
Heard: NewPeerKadIDSlice(heard),
|
||||
Waiting: NewPeerKadIDSlice(waiting),
|
||||
Queried: NewPeerKadIDSlice(queried),
|
||||
Unreachable: NewPeerKadIDSlice(unreachable),
|
||||
}
|
||||
}
|
||||
|
||||
// LookupUpdateEvent describes a lookup state update event.
|
||||
type LookupUpdateEvent struct {
|
||||
// Cause is the peer whose response (or lack of response) caused the update event.
|
||||
// If Cause is nil, this is the first update event in the lookup, caused by the seeding.
|
||||
Cause *PeerKadID
|
||||
// Source is the peer who informed us about the peer IDs in this update (below).
|
||||
Source *PeerKadID
|
||||
// Heard is a set of peers whose state in the lookup's peerset is being set to "heard".
|
||||
Heard []*PeerKadID
|
||||
// Waiting is a set of peers whose state in the lookup's peerset is being set to "waiting".
|
||||
Waiting []*PeerKadID
|
||||
// Queried is a set of peers whose state in the lookup's peerset is being set to "queried".
|
||||
Queried []*PeerKadID
|
||||
// Unreachable is a set of peers whose state in the lookup's peerset is being set to "unreachable".
|
||||
Unreachable []*PeerKadID
|
||||
}
|
||||
|
||||
// LookupTerminateEvent describes a lookup termination event.
|
||||
type LookupTerminateEvent struct {
|
||||
// Reason is the reason for lookup termination.
|
||||
Reason LookupTerminationReason
|
||||
}
|
||||
|
||||
// NewLookupTerminateEvent creates a new lookup termination event with a given reason.
|
||||
func NewLookupTerminateEvent(reason LookupTerminationReason) *LookupTerminateEvent {
|
||||
return &LookupTerminateEvent{Reason: reason}
|
||||
}
|
||||
|
||||
// LookupTerminationReason captures reasons for terminating a lookup.
|
||||
type LookupTerminationReason int
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the passed lookup termination reason.
|
||||
func (r LookupTerminationReason) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(r.String())
|
||||
}
|
||||
|
||||
func (r LookupTerminationReason) String() string {
|
||||
switch r {
|
||||
case LookupStopped:
|
||||
return "stopped"
|
||||
case LookupCancelled:
|
||||
return "cancelled"
|
||||
case LookupStarvation:
|
||||
return "starvation"
|
||||
case LookupCompleted:
|
||||
return "completed"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
const (
|
||||
// LookupStopped indicates that the lookup was aborted by the user's stopFn.
|
||||
LookupStopped LookupTerminationReason = iota
|
||||
// LookupCancelled indicates that the lookup was aborted by the context.
|
||||
LookupCancelled
|
||||
// LookupStarvation indicates that the lookup terminated due to lack of unqueried peers.
|
||||
LookupStarvation
|
||||
// LookupCompleted indicates that the lookup terminated successfully, reaching the Kademlia end condition.
|
||||
LookupCompleted
|
||||
)
|
||||
|
||||
type routingLookupKey struct{}
|
||||
|
||||
// TODO: lookupEventChannel copies the implementation of eventChanel.
|
||||
// The two should be refactored to use a common event channel implementation.
|
||||
// A common implementation needs to rethink the signature of RegisterForEvents,
|
||||
// because returning a typed channel cannot be made polymorphic without creating
|
||||
// additional "adapter" channels. This will be easier to handle when Go
|
||||
// introduces generics.
|
||||
type lookupEventChannel struct {
|
||||
mu sync.Mutex
|
||||
ctx context.Context
|
||||
ch chan<- *LookupEvent
|
||||
}
|
||||
|
||||
// waitThenClose is spawned in a goroutine when the channel is registered. This
|
||||
// safely cleans up the channel when the context has been canceled.
|
||||
func (e *lookupEventChannel) waitThenClose() {
|
||||
<-e.ctx.Done()
|
||||
e.mu.Lock()
|
||||
close(e.ch)
|
||||
// 1. Signals that we're done.
|
||||
// 2. Frees memory (in case we end up hanging on to this for a while).
|
||||
e.ch = nil
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// send sends an event on the event channel, aborting if either the passed or
|
||||
// the internal context expire.
|
||||
func (e *lookupEventChannel) send(ctx context.Context, ev *LookupEvent) {
|
||||
e.mu.Lock()
|
||||
// Closed.
|
||||
if e.ch == nil {
|
||||
e.mu.Unlock()
|
||||
return
|
||||
}
|
||||
// in case the passed context is unrelated, wait on both.
|
||||
select {
|
||||
case e.ch <- ev:
|
||||
case <-e.ctx.Done():
|
||||
case <-ctx.Done():
|
||||
}
|
||||
e.mu.Unlock()
|
||||
}
|
||||
|
||||
// RegisterForLookupEvents registers a lookup event channel with the given context.
|
||||
// The returned context can be passed to DHT queries to receive lookup events on
|
||||
// the returned channels.
|
||||
//
|
||||
// The passed context MUST be canceled when the caller is no longer interested
|
||||
// in query events.
|
||||
func RegisterForLookupEvents(ctx context.Context) (context.Context, <-chan *LookupEvent) {
|
||||
ch := make(chan *LookupEvent, LookupEventBufferSize)
|
||||
ech := &lookupEventChannel{ch: ch, ctx: ctx}
|
||||
go ech.waitThenClose()
|
||||
return context.WithValue(ctx, routingLookupKey{}, ech), ch
|
||||
}
|
||||
|
||||
// LookupEventBufferSize is the number of events to buffer.
|
||||
var LookupEventBufferSize = 16
|
||||
|
||||
// PublishLookupEvent publishes a query event to the query event channel
|
||||
// associated with the given context, if any.
|
||||
func PublishLookupEvent(ctx context.Context, ev *LookupEvent) {
|
||||
ich := ctx.Value(routingLookupKey{})
|
||||
if ich == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// We *want* to panic here.
|
||||
ech := ich.(*lookupEventChannel)
|
||||
ech.send(ctx, ev)
|
||||
}
|
48
go-libp2p-kad-dht/ext_test.go
Normal file
48
go-libp2p-kad-dht/ext_test.go
Normal file
@ -0,0 +1,48 @@
|
||||
package dht
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/network"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||
)
|
||||
|
||||
func TestInvalidRemotePeers(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
mn, err := mocknet.FullMeshLinked(5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer mn.Close()
|
||||
hosts := mn.Hosts()
|
||||
|
||||
os := []Option{testPrefix, DisableAutoRefresh(), Mode(ModeServer)}
|
||||
d, err := New(ctx, hosts[0], os...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, proto := range d.serverProtocols {
|
||||
// Hang on every request.
|
||||
hosts[1].SetStreamHandler(proto, func(s network.Stream) {
|
||||
defer s.Reset() // nolint
|
||||
<-ctx.Done()
|
||||
})
|
||||
}
|
||||
|
||||
err = mn.ConnectAllButSelf()
|
||||
if err != nil {
|
||||
t.Fatal("failed to connect peers", err)
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// hosts[1] isn't added to the routing table because it isn't responding to
|
||||
// the DHT request
|
||||
require.Equal(t, 0, d.routingTable.Size())
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user