diff --git a/esy.lock/index.json b/esy.lock/index.json index a07cce0..3ccee46 100644 --- a/esy.lock/index.json +++ b/esy.lock/index.json @@ -1,5 +1,5 @@ { - "checksum": "ffe903d165082ab60be24550d1dae31b", + "checksum": "9d770610ebf28d090c00e576f71d8236", "root": "pollinate@link-dev:./package.json", "node": { "pollinate@link-dev:./package.json": { @@ -19,6 +19,7 @@ "@opam/ppx_deriving@opam:5.2.1@089e5dd3", "@opam/ppx_compare@opam:v0.14.0@fbd22977", "@opam/ppx_bin_prot@opam:v0.14.0@4a83bcd2", + "@opam/odoc@opam:2.1.0@d39daa6f", "@opam/lwt_ppx@opam:2.0.3@125707d0", "@opam/lwt@opam:5.5.0@30354e4c", "@opam/dune@opam:2.9.3@f57a6d69", "@opam/bin_prot@opam:v0.14.0@2d7601e9" @@ -64,13 +65,13 @@ }, "overrides": [], "dependencies": [ - "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@1ea9f987", - "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.8@7e48217d", + "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.3@5d74d95b", + "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.9@db929a12", "@opam/biniou@opam:1.2.1@420bda02", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ - "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@1ea9f987", + "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.3@5d74d95b", "@opam/dune@opam:2.9.3@f57a6d69", "@opam/biniou@opam:1.2.1@420bda02" ] }, @@ -183,6 +184,34 @@ ], "devDependencies": [ "ocaml@4.13.1000@d41d8cd9" ] }, + "@opam/tyxml@opam:4.5.0@0a609297": { + "id": "@opam/tyxml@opam:4.5.0@0a609297", + "name": "@opam/tyxml", + "version": "opam:4.5.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/c6/c69accef5df4dd89d38f6aa0baad01e8fda4e9e98bb7dad61bec1452c5716068#sha256:c69accef5df4dd89d38f6aa0baad01e8fda4e9e98bb7dad61bec1452c5716068", + "archive:https://github.com/ocsigen/tyxml/releases/download/4.5.0/tyxml-4.5.0.tbz#sha256:c69accef5df4dd89d38f6aa0baad01e8fda4e9e98bb7dad61bec1452c5716068" + ], + "opam": { + "name": "tyxml", + "version": "4.5.0", + "path": "esy.lock/opam/tyxml.4.5.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", + "@opam/seq@opam:base@d8d7de1d", "@opam/re@opam:1.10.4@c4910ba6", + "@opam/dune@opam:2.9.3@f57a6d69", "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", + "@opam/seq@opam:base@d8d7de1d", "@opam/re@opam:1.10.4@c4910ba6", + "@opam/dune@opam:2.9.3@f57a6d69" + ] + }, "@opam/topkg@opam:1.0.5@0aa59f51": { "id": "@opam/topkg@opam:1.0.5@0aa59f51", "name": "@opam/topkg", @@ -301,20 +330,20 @@ "@opam/base@opam:v0.14.3@b3ddb868" ] }, - "@opam/spawn@opam:v0.15.0@4a27a4cb": { - "id": "@opam/spawn@opam:v0.15.0@4a27a4cb", + "@opam/spawn@opam:v0.15.1@85e9d6f1": { + "id": "@opam/spawn@opam:v0.15.1@85e9d6f1", "name": "@opam/spawn", - "version": "opam:v0.15.0", + "version": "opam:v0.15.1", "source": { "type": "install", "source": [ - "archive:https://opam.ocaml.org/cache/sha256/31/310fb2a50ac7f64c738182cbabd9d27c1aeae1a08107fe14da8d35a87cbb57c7#sha256:310fb2a50ac7f64c738182cbabd9d27c1aeae1a08107fe14da8d35a87cbb57c7", - "archive:https://github.com/janestreet/spawn/archive/v0.15.0.tar.gz#sha256:310fb2a50ac7f64c738182cbabd9d27c1aeae1a08107fe14da8d35a87cbb57c7" + "archive:https://opam.ocaml.org/cache/sha256/9a/9afdee314fab6c3fcd689ab6eb5608d6b78078e6dede3953a47debde06c19d50#sha256:9afdee314fab6c3fcd689ab6eb5608d6b78078e6dede3953a47debde06c19d50", + "archive:https://github.com/janestreet/spawn/archive/v0.15.1.tar.gz#sha256:9afdee314fab6c3fcd689ab6eb5608d6b78078e6dede3953a47debde06c19d50" ], "opam": { "name": "spawn", - "version": "v0.15.0", - "path": "esy.lock/opam/spawn.v0.15.0" + "version": "v0.15.1", + "path": "esy.lock/opam/spawn.v0.15.1" } }, "overrides": [], @@ -370,12 +399,12 @@ "overrides": [], "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/sexplib0@opam:v0.14.0@155c136c", - "@opam/parsexp@opam:v0.14.2@1d15b9d2", "@opam/num@opam:1.4@15ff926d", + "@opam/parsexp@opam:v0.14.2@1d15b9d2", "@opam/num@opam:1.4@54b259a0", "@opam/dune@opam:2.9.3@f57a6d69", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/sexplib0@opam:v0.14.0@155c136c", - "@opam/parsexp@opam:v0.14.2@1d15b9d2", "@opam/num@opam:1.4@15ff926d", + "@opam/parsexp@opam:v0.14.2@1d15b9d2", "@opam/num@opam:1.4@54b259a0", "@opam/dune@opam:2.9.3@f57a6d69" ] }, @@ -448,20 +477,20 @@ "ocaml@4.13.1000@d41d8cd9", "@opam/dune@opam:2.9.3@f57a6d69" ] }, - "@opam/re@opam:1.10.3@0585c65d": { - "id": "@opam/re@opam:1.10.3@0585c65d", + "@opam/re@opam:1.10.4@c4910ba6": { + "id": "@opam/re@opam:1.10.4@c4910ba6", "name": "@opam/re", - "version": "opam:1.10.3", + "version": "opam:1.10.4", "source": { "type": "install", "source": [ - "archive:https://opam.ocaml.org/cache/sha256/84/846546967f3fe31765935dd40a6460a9424337ecce7b12727fcba49480790ebb#sha256:846546967f3fe31765935dd40a6460a9424337ecce7b12727fcba49480790ebb", - "archive:https://github.com/ocaml/ocaml-re/releases/download/1.10.3/re-1.10.3.tbz#sha256:846546967f3fe31765935dd40a6460a9424337ecce7b12727fcba49480790ebb" + "archive:https://opam.ocaml.org/cache/sha256/83/83eb3e4300aa9b1dc7820749010f4362ea83524742130524d78c20ce99ca747c#sha256:83eb3e4300aa9b1dc7820749010f4362ea83524742130524d78c20ce99ca747c", + "archive:https://github.com/ocaml/ocaml-re/releases/download/1.10.4/re-1.10.4.tbz#sha256:83eb3e4300aa9b1dc7820749010f4362ea83524742130524d78c20ce99ca747c" ], "opam": { "name": "re", - "version": "1.10.3", - "path": "esy.lock/opam/re.1.10.3" + "version": "1.10.4", + "path": "esy.lock/opam/re.1.10.4" } }, "overrides": [], @@ -943,7 +972,7 @@ "@opam/ppxlib@opam:0.25.0@8553d2e8", "@opam/ppx_derivers@opam:1.2.1@e2cbad12", "@opam/ocamlfind@opam:1.9.3@781b30f3", - "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.8@7e48217d", + "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.9@db929a12", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ @@ -1279,6 +1308,43 @@ "@opam/dune@opam:2.9.3@f57a6d69", "@opam/astring@opam:0.8.5@1300cee8" ] }, + "@opam/odoc@opam:2.1.0@d39daa6f": { + "id": "@opam/odoc@opam:2.1.0@d39daa6f", + "name": "@opam/odoc", + "version": "opam:2.1.0", + "source": { + "type": "install", + "source": [ + "archive:https://opam.ocaml.org/cache/sha256/65/65a2523a50ee368164f1f24f75866a6a36cdb0d00039c3006ec824351d4e4967#sha256:65a2523a50ee368164f1f24f75866a6a36cdb0d00039c3006ec824351d4e4967", + "archive:https://github.com/ocaml/odoc/releases/download/2.1.0/odoc-2.1.0.tbz#sha256:65a2523a50ee368164f1f24f75866a6a36cdb0d00039c3006ec824351d4e4967" + ], + "opam": { + "name": "odoc", + "version": "2.1.0", + "path": "esy.lock/opam/odoc.2.1.0" + } + }, + "overrides": [], + "dependencies": [ + "ocaml@4.13.1000@d41d8cd9", "@opam/tyxml@opam:4.5.0@0a609297", + "@opam/result@opam:1.5@1c6a6533", + "@opam/odoc-parser@opam:1.0.0@b1029bdf", + "@opam/fpath@opam:0.7.3@674d8125", "@opam/fmt@opam:0.9.0@87213963", + "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.9@db929a12", + "@opam/cmdliner@opam:1.1.1@03763729", + "@opam/astring@opam:0.8.5@1300cee8", + "@esy-ocaml/substs@0.0.1@d41d8cd9" + ], + "devDependencies": [ + "ocaml@4.13.1000@d41d8cd9", "@opam/tyxml@opam:4.5.0@0a609297", + "@opam/result@opam:1.5@1c6a6533", + "@opam/odoc-parser@opam:1.0.0@b1029bdf", + "@opam/fpath@opam:0.7.3@674d8125", "@opam/fmt@opam:0.9.0@87213963", + "@opam/dune@opam:2.9.3@f57a6d69", + "@opam/cmdliner@opam:1.1.1@03763729", + "@opam/astring@opam:0.8.5@1300cee8" + ] + }, "@opam/octavius@opam:1.2.2@2205cc65": { "id": "@opam/octavius@opam:1.2.2@2205cc65", "name": "@opam/octavius", @@ -1323,7 +1389,7 @@ "overrides": [], "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/dune@opam:2.9.3@f57a6d69", - "@opam/cppo@opam:1.6.8@7e48217d", + "@opam/cppo@opam:1.6.9@db929a12", "@opam/base-bytes@opam:base@19d0c2ff", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], @@ -1410,7 +1476,7 @@ "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", "@opam/uuseg@opam:14.0.0@7d21466b", - "@opam/stdio@opam:v0.14.0@a5affb43", "@opam/re@opam:1.10.3@0585c65d", + "@opam/stdio@opam:v0.14.0@a5affb43", "@opam/re@opam:1.10.4@c4910ba6", "@opam/odoc-parser@opam:1.0.0@b1029bdf", "@opam/ocp-indent@opam:1.8.1@e32a3c50", "@opam/ocaml-version@opam:3.4.0@b6cd49e1", @@ -1429,7 +1495,7 @@ "devDependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", "@opam/uuseg@opam:14.0.0@7d21466b", - "@opam/stdio@opam:v0.14.0@a5affb43", "@opam/re@opam:1.10.3@0585c65d", + "@opam/stdio@opam:v0.14.0@a5affb43", "@opam/re@opam:1.10.4@c4910ba6", "@opam/odoc-parser@opam:1.0.0@b1029bdf", "@opam/ocp-indent@opam:1.8.1@e32a3c50", "@opam/ocaml-version@opam:3.4.0@b6cd49e1", @@ -1597,8 +1663,8 @@ "overrides": [], "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@69d87312", - "@opam/spawn@opam:v0.15.0@4a27a4cb", - "@opam/result@opam:1.5@1c6a6533", "@opam/re@opam:1.10.3@0585c65d", + "@opam/spawn@opam:v0.15.1@85e9d6f1", + "@opam/result@opam:1.5@1c6a6533", "@opam/re@opam:1.10.4@c4910ba6", "@opam/ppx_yojson_conv_lib@opam:v0.15.0@773058a7", "@opam/pp@opam:1.1.2@89ad03b5", "@opam/ocamlformat-rpc-lib@opam:0.19.0@125cf11d", @@ -1608,8 +1674,8 @@ ], "devDependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/yojson@opam:1.7.0@69d87312", - "@opam/spawn@opam:v0.15.0@4a27a4cb", - "@opam/result@opam:1.5@1c6a6533", "@opam/re@opam:1.10.3@0585c65d", + "@opam/spawn@opam:v0.15.1@85e9d6f1", + "@opam/result@opam:1.5@1c6a6533", "@opam/re@opam:1.10.4@c4910ba6", "@opam/ppx_yojson_conv_lib@opam:v0.15.0@773058a7", "@opam/pp@opam:1.1.2@89ad03b5", "@opam/ocamlformat-rpc-lib@opam:0.19.0@125cf11d", @@ -1642,8 +1708,8 @@ "ocaml@4.13.1000@d41d8cd9", "@opam/dune@opam:2.9.3@f57a6d69" ] }, - "@opam/num@opam:1.4@15ff926d": { - "id": "@opam/num@opam:1.4@15ff926d", + "@opam/num@opam:1.4@54b259a0": { + "id": "@opam/num@opam:1.4@54b259a0", "name": "@opam/num", "version": "opam:1.4", "source": { @@ -1826,7 +1892,7 @@ "@opam/ocaml-syntax-shims@opam:1.0.0@9f361fbb", "@opam/mmap@opam:1.2.0@b0f60a84", "@opam/dune-configurator@opam:2.9.3@174e411b", - "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.8@7e48217d", + "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cppo@opam:1.6.9@db929a12", "@opam/base-unix@opam:base@87d0b2eb", "@opam/base-threads@opam:base@36803084", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -2054,23 +2120,28 @@ ], "devDependencies": [ "@opam/dune@opam:2.9.3@f57a6d69" ] }, - "@opam/easy-format@opam:1.3.2@1ea9f987": { - "id": "@opam/easy-format@opam:1.3.2@1ea9f987", + "@opam/easy-format@opam:1.3.3@5d74d95b": { + "id": "@opam/easy-format@opam:1.3.3@5d74d95b", "name": "@opam/easy-format", - "version": "opam:1.3.2", + "version": "opam:1.3.3", "source": { "type": "install", "source": [ - "archive:https://opam.ocaml.org/cache/sha256/34/3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926#sha256:3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926", - "archive:https://github.com/mjambon/easy-format/releases/download/1.3.2/easy-format-1.3.2.tbz#sha256:3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926" + "archive:https://opam.ocaml.org/cache/sha256/ea/eafccae911c26ca23e4ddacee3eaa54654d20f973b8680f84b708cef43adc416#sha256:eafccae911c26ca23e4ddacee3eaa54654d20f973b8680f84b708cef43adc416", + "archive:https://github.com/mjambon/easy-format/releases/download/1.3.3/easy-format-1.3.3.tbz#sha256:eafccae911c26ca23e4ddacee3eaa54654d20f973b8680f84b708cef43adc416" ], "opam": { "name": "easy-format", - "version": "1.3.2", - "path": "esy.lock/opam/easy-format.1.3.2" + "version": "1.3.3", + "path": "esy.lock/opam/easy-format.1.3.3" } }, - "overrides": [], + "overrides": [ + { + "opamoverride": + "esy.lock/overrides/opam__s__easy_format_opam__c__1.3.3_opam_override" + } + ], "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/dune@opam:2.9.3@f57a6d69", "@esy-ocaml/substs@0.0.1@d41d8cd9" @@ -2180,20 +2251,20 @@ "ocaml@4.13.1000@d41d8cd9", "@opam/dune@opam:2.9.3@f57a6d69" ] }, - "@opam/cppo@opam:1.6.8@7e48217d": { - "id": "@opam/cppo@opam:1.6.8@7e48217d", + "@opam/cppo@opam:1.6.9@db929a12": { + "id": "@opam/cppo@opam:1.6.9@db929a12", "name": "@opam/cppo", - "version": "opam:1.6.8", + "version": "opam:1.6.9", "source": { "type": "install", "source": [ - "archive:https://opam.ocaml.org/cache/md5/fe/fed401197d86f9089e89f6cbdf1d660d#md5:fed401197d86f9089e89f6cbdf1d660d", - "archive:https://github.com/ocaml-community/cppo/archive/v1.6.8.tar.gz#md5:fed401197d86f9089e89f6cbdf1d660d" + "archive:https://opam.ocaml.org/cache/md5/d2/d23ffe85ac7dc8f0afd1ddf622770d09#md5:d23ffe85ac7dc8f0afd1ddf622770d09", + "archive:https://github.com/ocaml-community/cppo/archive/v1.6.9.tar.gz#md5:d23ffe85ac7dc8f0afd1ddf622770d09" ], "opam": { "name": "cppo", - "version": "1.6.8", - "path": "esy.lock/opam/cppo.1.6.8" + "version": "1.6.9", + "path": "esy.lock/opam/cppo.1.6.9" } }, "overrides": [], @@ -2283,11 +2354,11 @@ }, "overrides": [], "dependencies": [ - "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@1ea9f987", + "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.3@5d74d95b", "@opam/dune@opam:2.9.3@f57a6d69", "@esy-ocaml/substs@0.0.1@d41d8cd9" ], "devDependencies": [ - "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.2@1ea9f987", + "ocaml@4.13.1000@d41d8cd9", "@opam/easy-format@opam:1.3.3@5d74d95b", "@opam/dune@opam:2.9.3@f57a6d69" ] }, @@ -2515,7 +2586,7 @@ "dependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", "@opam/stdlib-shims@opam:0.3.0@72c7bc98", - "@opam/re@opam:1.10.3@0585c65d", + "@opam/re@opam:1.10.4@c4910ba6", "@opam/ocaml-syntax-shims@opam:1.0.0@9f361fbb", "@opam/fmt@opam:0.9.0@87213963", "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cmdliner@opam:1.1.1@03763729", @@ -2525,7 +2596,7 @@ "devDependencies": [ "ocaml@4.13.1000@d41d8cd9", "@opam/uutf@opam:1.0.3@47c95a18", "@opam/stdlib-shims@opam:0.3.0@72c7bc98", - "@opam/re@opam:1.10.3@0585c65d", + "@opam/re@opam:1.10.4@c4910ba6", "@opam/ocaml-syntax-shims@opam:1.0.0@9f361fbb", "@opam/fmt@opam:0.9.0@87213963", "@opam/dune@opam:2.9.3@f57a6d69", "@opam/cmdliner@opam:1.1.1@03763729", diff --git a/esy.lock/opam/cppo.1.6.8/opam b/esy.lock/opam/cppo.1.6.9/opam similarity index 58% rename from esy.lock/opam/cppo.1.6.8/opam rename to esy.lock/opam/cppo.1.6.9/opam index c9d7f68..9c51ec6 100644 --- a/esy.lock/opam/cppo.1.6.8/opam +++ b/esy.lock/opam/cppo.1.6.9/opam @@ -1,37 +1,39 @@ opam-version: "2.0" -maintainer: "martin@mjambon.com" +synopsis: "Code preprocessor like cpp for OCaml" +description: """\ +Cppo is an equivalent of the C preprocessor for OCaml programs. +It allows the definition of simple macros and file inclusion. + +Cppo is: + +* more OCaml-friendly than cpp +* easy to learn without consulting a manual +* reasonably fast +* simple to install and to maintain""" +maintainer: [ + "Martin Jambon " "Yishuai Li " +] authors: "Martin Jambon" license: "BSD-3-Clause" homepage: "https://github.com/ocaml-community/cppo" -doc: "https://ocaml-community.github.io/cppo/" +doc: "https://ocaml-community.github.io/cppo" bug-reports: "https://github.com/ocaml-community/cppo/issues" depends: [ "ocaml" {>= "4.02.3"} - "dune" {>= "1.0"} + "dune" {>= "1.10"} "base-unix" ] build: [ ["dune" "subst"] {dev} ["dune" "build" "-p" name "-j" jobs] ["dune" "runtest" "-p" name "-j" jobs] {with-test} + ["dune" "build" "-p" name "@doc"] {with-doc} ] dev-repo: "git+https://github.com/ocaml-community/cppo.git" -synopsis: "Code preprocessor like cpp for OCaml" -description: """ -Cppo is an equivalent of the C preprocessor for OCaml programs. -It allows the definition of simple macros and file inclusion. - -Cppo is: - -* more OCaml-friendly than cpp -* easy to learn without consulting a manual -* reasonably fast -* simple to install and to maintain -""" url { - src: "https://github.com/ocaml-community/cppo/archive/v1.6.8.tar.gz" + src: "https://github.com/ocaml-community/cppo/archive/v1.6.9.tar.gz" checksum: [ - "md5=fed401197d86f9089e89f6cbdf1d660d" - "sha512=069bbe0ef09c03b0dc4b5795f909c3ef872fe99c6f1e6704a0fa97594b1570b3579226ec67fe11d696ccc349a4585055bbaf07c65eff423aa45af28abf38c858" + "md5=d23ffe85ac7dc8f0afd1ddf622770d09" + "sha512=26ff5a7b7f38c460661974b23ca190f0feae3a99f1974e0fd12ccf08745bd7d91b7bc168c70a5385b837bfff9530e0e4e41cf269f23dd8cf16ca658008244b44" ] -} +} \ No newline at end of file diff --git a/esy.lock/opam/easy-format.1.3.2/opam b/esy.lock/opam/easy-format.1.3.3/opam similarity index 68% rename from esy.lock/opam/easy-format.1.3.2/opam rename to esy.lock/opam/easy-format.1.3.3/opam index f55c2c8..5926060 100644 --- a/esy.lock/opam/easy-format.1.3.2/opam +++ b/esy.lock/opam/easy-format.1.3.3/opam @@ -1,17 +1,4 @@ opam-version: "2.0" -build: [ - ["dune" "subst"] {dev} - ["dune" "build" "-p" name "-j" jobs] - ["dune" "runtest" "-p" name "-j" jobs] {with-test} - ["dune" "build" "-p" name "@doc"] {with-doc} -] -maintainer: ["martin@mjambon.com" "rudi.grinberg@gmail.com"] -authors: ["Martin Jambon"] -bug-reports: "https://github.com/mjambon/easy-format/issues" -homepage: "https://github.com/mjambon/easy-format" -doc: "https://mjambon.github.io/easy-format/" -license: "BSD-3-Clause" -dev-repo: "git+https://github.com/mjambon/easy-format.git" synopsis: "High-level and functional interface to the Format module of the OCaml standard library" description: """ @@ -32,15 +19,40 @@ nodes: Atoms represent any text that is guaranteed to be printed as-is. Lists can model any sequence of items such as arrays of data or lists of definitions that are labelled with something like "int main", "let x =" or "x:".""" +maintainer: ["martin@mjambon.com" "rudi.grinberg@gmail.com"] +authors: ["Martin Jambon"] +license: "BSD-3-Clause" +homepage: "https://github.com/mjambon/easy-format" +doc: "https://mjambon.github.io/easy-format/" +bug-reports: "https://github.com/mjambon/easy-format/issues" depends: [ - "dune" {>= "1.10"} - "ocaml" {>= "4.02.3"} + "dune" {>= "2.9"} + "ocaml" {>= "4.08"} + "odoc" {with-doc} ] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "--promote-install-files=false" + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] + ["dune" "install" "-p" name "--create-install-files" name] +] +dev-repo: "git+https://github.com/mjambon/easy-format.git" url { src: - "https://github.com/mjambon/easy-format/releases/download/1.3.2/easy-format-1.3.2.tbz" + "https://github.com/mjambon/easy-format/releases/download/1.3.3/easy-format-1.3.3.tbz" checksum: [ - "sha256=3440c2b882d537ae5e9011eb06abb53f5667e651ea4bb3b460ea8230fa8c1926" - "sha512=e39377a2ff020ceb9ac29e8515a89d9bdbc91dfcfa871c4e3baafa56753fac2896768e5d9822a050dc1e2ade43c8967afb69391a386c0a8ecd4e1f774e236135" + "sha256=eafccae911c26ca23e4ddacee3eaa54654d20f973b8680f84b708cef43adc416" + "sha512=611b3124f6a0ec6406b7bda8018a94c9c4a9da9d22495a5c34a6312bf7f0f0607a9529b276f7039ce3f3b15a955dac413d6d1229a55d5ac291302a3ddd5807e5" ] } +x-commit-hash: "56c57e69ef067d1cc4e31029d31e77e55b46be95" diff --git a/esy.lock/opam/num.1.4/opam b/esy.lock/opam/num.1.4/opam index 0e39879..253f84e 100644 --- a/esy.lock/opam/num.1.4/opam +++ b/esy.lock/opam/num.1.4/opam @@ -13,9 +13,13 @@ depends: [ conflicts: ["base-num"] build: make install: [ - make - "install" {!ocaml:preinstalled} - "findlib-install" {ocaml:preinstalled} + ["ocamlfind" "remove" "num"] + ["ocamlfind" "remove" "num-top"] + [ + make + "install" {!ocaml:preinstalled} + "findlib-install" {ocaml:preinstalled} + ] ] dev-repo: "git+https://github.com/ocaml/num.git" url { @@ -24,4 +28,4 @@ url { "md5=cda2b727e116a0b6a9c03902cc4b2415" "sha512=0cc9be8ad95704bb683b4bf6698bada1ee9a40dc05924b72adc7b969685c33eeb68ccf174cc09f6a228c48c18fe94af06f28bebc086a24973a066da620db8e6f" ] -} \ No newline at end of file +} diff --git a/esy.lock/opam/odoc.2.1.0/opam b/esy.lock/opam/odoc.2.1.0/opam new file mode 100644 index 0000000..6b1df40 --- /dev/null +++ b/esy.lock/opam/odoc.2.1.0/opam @@ -0,0 +1,60 @@ +opam-version: "2.0" +homepage: "http://github.com/ocaml/odoc" +doc: "https://ocaml.github.io/odoc/" +bug-reports: "https://github.com/ocaml/odoc/issues" +license: "ISC" + +authors: [ + "Thomas Refis " + "David Sheets " + "Leo White " + "Anton Bachin " + "Jon Ludlam " + "Jules Aguillon " + "Lubega Simon " +] +maintainer: "Jon Ludlam " +dev-repo: "git+https://github.com/ocaml/odoc.git" + +synopsis: "OCaml documentation generator" +description: """ +Odoc is a documentation generator for OCaml. It reads doc comments, +delimited with `(** ... *)`, and outputs HTML. +""" + +depends: [ + "odoc-parser" {>= "0.9.0"} + "astring" + "cmdliner" {>= "1.0.0"} + "cppo" {build & >= "1.1.0"} + "dune" {>= "2.9.1"} + "fpath" + "ocaml" {>= "4.02.0"} + "result" + "tyxml" {>= "4.3.0"} + "fmt" + + "ocamlfind" {with-test} + "yojson" {with-test} + ("ocaml" {< "4.04.1" & with-test} | "sexplib0" {with-test}) + "conf-jq" {with-test} + + "ppx_expect" {with-test} + "bos" {with-test} + + "bisect_ppx" {dev & > "2.5.0"} + ("ocaml" {< "4.03.0" & dev} | "mdx" {dev}) +] + +build: [ + ["dune" "subst"] {dev} + ["dune" "build" "-p" name "-j" jobs] +] +url { + src: "https://github.com/ocaml/odoc/releases/download/2.1.0/odoc-2.1.0.tbz" + checksum: [ + "sha256=65a2523a50ee368164f1f24f75866a6a36cdb0d00039c3006ec824351d4e4967" + "sha512=cf4d7e884b94a9b9c4bcb62d4423d7289d7bbbf2642c5eacf9577b76eb835cf6ecc79d2384d36d174d2e9d8f758b5082c0c4bf8f66b5c6db4e9805dc3fc9ee1a" + ] +} +x-commit-hash: "d654ee2a4ff3e1465dcf92b882c26de71f7a9986" diff --git a/esy.lock/opam/re.1.10.3/opam b/esy.lock/opam/re.1.10.4/opam similarity index 66% rename from esy.lock/opam/re.1.10.3/opam rename to esy.lock/opam/re.1.10.4/opam index c65d450..9dad661 100644 --- a/esy.lock/opam/re.1.10.3/opam +++ b/esy.lock/opam/re.1.10.4/opam @@ -8,19 +8,19 @@ authors: [ "Rudi Grinberg" "Gabriel Radanne" ] -license: "LGPL-2.0 with OCaml linking exception" +license: "LGPL-2.0-or-later WITH OCaml-LGPL-linking-exception" homepage: "https://github.com/ocaml/ocaml-re" bug-reports: "https://github.com/ocaml/ocaml-re/issues" dev-repo: "git+https://github.com/ocaml/ocaml-re.git" build: [ - ["dune" "subst"] {pinned} + ["dune" "subst"] {dev} ["dune" "build" "-p" name "-j" jobs] ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] depends: [ - "ocaml" {>= "4.02"} + "ocaml" {>= "4.03"} "dune" {>= "2.0"} "ounit" {with-test} "seq" @@ -37,10 +37,10 @@ Pure OCaml regular expressions with: """ url { src: - "https://github.com/ocaml/ocaml-re/releases/download/1.10.3/re-1.10.3.tbz" + "https://github.com/ocaml/ocaml-re/releases/download/1.10.4/re-1.10.4.tbz" checksum: [ - "sha256=846546967f3fe31765935dd40a6460a9424337ecce7b12727fcba49480790ebb" - "sha512=d02103b7b8b8d8bc797341dcc933554745427f3c1b51b54b4ac9ff81badfd68c94726c57548b08e00ca99f3e09741b54b6500e97c19fc0e8fcefd6dfbe71da7f" + "sha256=83eb3e4300aa9b1dc7820749010f4362ea83524742130524d78c20ce99ca747c" + "sha512=92b05cf92c389fa8c753f2acca837b15dd05a4a2e8e2bec7a269d2e14c35b1a786d394258376648f80b4b99250ba1900cfe68230b8385aeac153149d9ce56099" ] } -x-commit-hash: "c5d5df80e128c3d7646b7d8b1322012c5fcc35f3" +x-commit-hash: "e9a4cecb8294c1839db18b1d0c30e755ec85ed5e" diff --git a/esy.lock/opam/spawn.v0.15.0/opam b/esy.lock/opam/spawn.v0.15.1/opam similarity index 79% rename from esy.lock/opam/spawn.v0.15.0/opam rename to esy.lock/opam/spawn.v0.15.1/opam index d8d1578..5be3a99 100644 --- a/esy.lock/opam/spawn.v0.15.0/opam +++ b/esy.lock/opam/spawn.v0.15.1/opam @@ -45,12 +45,12 @@ build: [ ] ] dev-repo: "git+https://github.com/janestreet/spawn.git" -x-commit-hash: "b5a25cab2f53a5ee9e10a7b8a96506cc61ce1198" +x-commit-hash: "13d279ebfa8c40d4bafe18cddfdff0de54b4eaff" url { src: - "https://github.com/janestreet/spawn/archive/v0.15.0.tar.gz" + "https://github.com/janestreet/spawn/archive/v0.15.1.tar.gz" checksum: [ - "sha256=310fb2a50ac7f64c738182cbabd9d27c1aeae1a08107fe14da8d35a87cbb57c7" - "sha512=3a775b57a73efee6adbc30b32fa779f27d11c7008a46f90fdb9da6288533e2d83fc49dbcd770c087f2e4560c5586ff72a9a2985d8929955773cc10d83f126013" + "sha256=9afdee314fab6c3fcd689ab6eb5608d6b78078e6dede3953a47debde06c19d50" + "sha512=efdb31d5ec5ea36d0bc80224d4ee04e46ce3428d1662870e6cebece92bc313d6eebee378802c0c059dd6e0cafea515308c31b7dfaf04a098eb4566583c1e9ed4" ] } diff --git a/esy.lock/opam/tyxml.4.5.0/opam b/esy.lock/opam/tyxml.4.5.0/opam new file mode 100644 index 0000000..22c7760 --- /dev/null +++ b/esy.lock/opam/tyxml.4.5.0/opam @@ -0,0 +1,42 @@ +opam-version: "2.0" +synopsis: "A library for building correct HTML and SVG documents" +description: + "TyXML provides a set of convenient combinators that uses the OCaml type system to ensure the validity of the generated documents. TyXML can be used with any representation of HTML and SVG: the textual one, provided directly by this package, or DOM trees (`js_of_ocaml-tyxml`) virtual DOM (`virtual-dom`) and reactive or replicated trees (`eliom`). You can also create your own representation and use it to instantiate a new set of combinators." +maintainer: ["dev@ocsigen.org"] +authors: ["The ocsigen team"] +license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" +homepage: "https://github.com/ocsigen/tyxml" +doc: "https://ocsigen.org/tyxml/latest/manual/intro" +bug-reports: "https://github.com/ocsigen/tyxml/issues" +depends: [ + "dune" {>= "2.0"} + "ocaml" {>= "4.02"} + "alcotest" {with-test} + "re" {>= "1.5.0"} + "seq" + "uutf" {>= "1.0.0"} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/ocsigen/tyxml.git" +x-commit-hash: "ef431a4bceaefb2d9248e79092e6c1a1a9420095" +url { + src: + "https://github.com/ocsigen/tyxml/releases/download/4.5.0/tyxml-4.5.0.tbz" + checksum: [ + "sha256=c69accef5df4dd89d38f6aa0baad01e8fda4e9e98bb7dad61bec1452c5716068" + "sha512=772535441b09c393d53c27152e65f404a0a541aa0cea1bda899a8d751ab64d1729237e583618c3ff33d75e3865d53503d1ea413c6bbc8c68c413347efd1709b3" + ] +} diff --git a/esy.lock/overrides/opam__s__easy_format_opam__c__1.3.3_opam_override/package.json b/esy.lock/overrides/opam__s__easy_format_opam__c__1.3.3_opam_override/package.json new file mode 100644 index 0000000..f9bd9e0 --- /dev/null +++ b/esy.lock/overrides/opam__s__easy_format_opam__c__1.3.3_opam_override/package.json @@ -0,0 +1,3 @@ +{ + "build": "dune build -p easy-format" +} diff --git a/flake.lock b/flake.lock index 14acee9..ed0ef05 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1652372896, - "narHash": "sha256-lURGussfF3mGrFPQT3zgW7+RC0pBhbHzco0C7I+ilow=", + "lastModified": 1653893745, + "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "0d347c56f6f41de822a4f4c7ff5072f3382db121", + "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nix-filter": { "locked": { - "lastModified": 1649838635, - "narHash": "sha256-P1h48+l9vUvMz4JwHBgkTXiX6mE8oYR75vBVUbe6Cuc=", + "lastModified": 1653590866, + "narHash": "sha256-E4yKIrt/S//WfW5D9IhQ1dVuaAy8RE7EiCMfnbrOC78=", "owner": "numtide", "repo": "nix-filter", - "rev": "40a58baa248a8b335e2d66ca258a74248af9d834", + "rev": "3e81a637cdf9f6e9b39aeb4d6e6394d1ad158e16", "type": "github" }, "original": { @@ -32,11 +32,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1652231724, - "narHash": "sha256-MjalcXFZgcgchp4QqnF05JTkFBBGad5hbksA1EKoP98=", + "lastModified": 1653931853, + "narHash": "sha256-O3wncIouj9x7gBPntzHeK/Hkmm9M1SGlYq7JI7saTAE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "41ff747f882914c1f8c233207ce280ac9d0c867f", + "rev": "f1c167688a6f81f4a51ab542e5f476c8c595e457", "type": "github" }, "original": { @@ -46,7 +46,7 @@ "type": "github" } }, - "ocaml-overlay": { + "ocaml-overlays": { "inputs": { "flake-utils": [ "flake-utils" @@ -56,11 +56,11 @@ ] }, "locked": { - "lastModified": 1652354587, - "narHash": "sha256-4wAlDsnfdAzOEM8WGVs1HDDqB8x2tJWQd0Z/86V3q1Y=", + "lastModified": 1654150159, + "narHash": "sha256-/SbOxDGuJr+aQHtBg+V5J+3OV+/gU/cZVOlEl8/kzG4=", "owner": "anmonteiro", "repo": "nix-overlays", - "rev": "a5d810ae91095586c18d2cafde80381936a445ad", + "rev": "70165a4922ed0f4819fac34ee4879604d754af6e", "type": "github" }, "original": { @@ -74,7 +74,7 @@ "flake-utils": "flake-utils", "nix-filter": "nix-filter", "nixpkgs": "nixpkgs", - "ocaml-overlay": "ocaml-overlay" + "ocaml-overlays": "ocaml-overlays" } } }, diff --git a/flake.nix b/flake.nix index 3284eab..e1c7c3c 100644 --- a/flake.nix +++ b/flake.nix @@ -4,19 +4,21 @@ flake-utils.url = "github:numtide/flake-utils"; nix-filter.url = "github:numtide/nix-filter"; - ocaml-overlay.url = "github:anmonteiro/nix-overlays"; - ocaml-overlay.inputs.nixpkgs.follows = "nixpkgs"; - ocaml-overlay.inputs.flake-utils.follows = "flake-utils"; + ocaml-overlays.url = "github:anmonteiro/nix-overlays"; + ocaml-overlays.inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; }; - outputs = { self, nixpkgs, flake-utils, nix-filter, ocaml-overlay }: + outputs = { self, nixpkgs, flake-utils, nix-filter, ocaml-overlays }: let supported_ocaml_versions = [ "ocamlPackages_4_13" "ocamlPackages_5_00" ]; out = system: let pkgs = import nixpkgs { inherit system; - overlays = [ ocaml-overlay.overlays."${system}".default ]; + overlays = [ ocaml-overlays.overlays."${system}".default ]; }; ocamlPackages_dev = pkgs.ocaml-ng.ocamlPackages_5_00; pollinate = (pkgs.callPackage ./nix { diff --git a/lib/common/tqueue.ml b/lib/common/tqueue.ml deleted file mode 100644 index f7462e9..0000000 --- a/lib/common/tqueue.ml +++ /dev/null @@ -1,68 +0,0 @@ -type 'a t = { - queue : 'a Queue.t; - lock : Lwt_mutex.t; - has_elt : unit Lwt_condition.t; -} - -let create () = - let queue = Queue.create () in - let lock = Lwt_mutex.create () in - let has_elt = Lwt_condition.create () in - { queue; lock; has_elt } - -let add x { queue; lock; has_elt } = - Lwt_mutex.with_lock lock (fun () -> - Queue.add x queue; - let%lwt () = - if Queue.length queue = 1 then ( - Lwt_condition.signal has_elt (); - Lwt.return ()) - else - Lwt.return () in - Lwt.return ()) - -let push = add - -let take { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> Lwt.return (Queue.take_opt queue)) - -let wait_to_take { queue; lock; has_elt } = - Lwt_mutex.with_lock lock (fun () -> - let%lwt () = - if Queue.is_empty queue then - Lwt_condition.wait ~mutex:lock has_elt - else - Lwt.return () in - Lwt.return (Queue.take queue)) - -let pop = take - -let peek { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> Lwt.return (Queue.peek_opt queue)) - -let wait_to_peek { queue; lock; has_elt } = - Lwt_mutex.with_lock lock (fun () -> - let%lwt () = - if Queue.is_empty queue then - Lwt_condition.wait ~mutex:lock has_elt - else - Lwt.return () in - Lwt.return (Queue.peek queue)) - -let top = peek - -let clear { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> Lwt.return (Queue.clear queue)) - -let copy { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> - let queue' = queue in - let lock' = Lwt_mutex.create () in - let has_elt' = Lwt_condition.create () in - Lwt.return { queue = queue'; lock = lock'; has_elt = has_elt' }) - -let is_empty { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> Lwt.return (Queue.is_empty queue)) - -let length { queue; lock; _ } = - Lwt_mutex.with_lock lock (fun () -> Lwt.return (Queue.length queue)) diff --git a/lib/common/tqueue.mli b/lib/common/tqueue.mli deleted file mode 100644 index c8b2e8f..0000000 --- a/lib/common/tqueue.mli +++ /dev/null @@ -1,54 +0,0 @@ -(** Thread-safe queues with the same exact interface -as OCaml's Queue module, except any function that -reads from or writes to a [Queue] returns a promise. -Furthermore, there is a special take function for blocking -the current thread until an element is available to take -from the queue. -Read the {{:https://ocaml.org/api/Queue.html}documentation for OCaml's Queue module} -for more information about the functions in this module *) - -(** {1 Type} *) - -(** The type of a thread-safe queue *) -type 'a t - -(** {1 API} *) - -(** Blocks the current thread of execution until -an element is in the queue, then returns a promise -containing the element. *) -val wait_to_take : 'a t -> 'a Lwt.t - -(** Similar to [wait_to_take] but doesn't block - the current thread. *) -val take : 'a t -> 'a option Lwt.t - -(** Similar to [wait_to_take], but doesn't remove -from the queue. *) -val wait_to_peek : 'a t -> 'a Lwt.t - -(** Similar to [wait_to_peek] but doesn't block - the current thread. *) -val peek : 'a t -> 'a option Lwt.t - -(**/**) - -val create : unit -> 'a t - -val add : 'a -> 'a t -> unit Lwt.t - -val push : 'a -> 'a t -> unit Lwt.t - -val pop : 'a t -> 'a option Lwt.t - -val top : 'a t -> 'a option Lwt.t - -val clear : 'a t -> unit Lwt.t - -val copy : 'a t -> 'a t Lwt.t - -val is_empty : 'a t -> bool Lwt.t - -val length : 'a t -> int Lwt.t - -(**/**) diff --git a/lib/common/util.ml b/lib/common/util.ml index 8764fe2..e44cf12 100644 --- a/lib/common/util.ml +++ b/lib/common/util.ml @@ -45,7 +45,8 @@ end module Net = struct let create_socket port = let open Lwt_unix in - let ssock = socket ~cloexec:true PF_INET SOCK_DGRAM 0 in + let ssock = socket ~cloexec:false PF_INET SOCK_DGRAM 0 in + (* Printf.printf "Socket successfully opened on port: %d" port; *) let addr = ADDR_INET (Unix.inet_addr_loopback, port) in let%lwt () = bind ssock addr in return ssock diff --git a/lib/common/util.mli b/lib/common/util.mli index 6767ca1..c75b9b3 100644 --- a/lib/common/util.mli +++ b/lib/common/util.mli @@ -10,7 +10,8 @@ val ( let* ) : 'a option -> ('a -> 'b option) -> 'b option module Encoding : sig (** Defines utilities for encoding or decoding messages. *) - (** The {i int} value of the buffer size. *) + (** The {i int} value of the necessary buffer size for storing the size + header at the beginning of each Bin_prot payload. *) val size_header_length : int (** Reads the value of the size header prepended diff --git a/lib/dune b/lib/dune index f5c1898..71009f1 100644 --- a/lib/dune +++ b/lib/dune @@ -1,4 +1,4 @@ (library (name pollinate) (public_name pollinate) - (libraries pollinate.common pollinate.node)) + (libraries pollinate.common pollinate.pnode)) diff --git a/lib/node/client.ml b/lib/node/client.ml deleted file mode 100644 index cecfa89..0000000 --- a/lib/node/client.ml +++ /dev/null @@ -1,86 +0,0 @@ -open Lwt_unix -open Common -open Common.Util -open Types - -let address_of { address; _ } = address - -let peer_from { address; peers; _ } = - Peer. - { - address; - status = Alive; - last_suspicious_status = None; - neighbors = peers; - } - -let add_peer node (peer : Peer.t) = - Base.Hashtbl.add node.peers ~key:peer.address ~data:peer - -let create_request node recipient payload = - Mutex.with_lock !node.current_request_id (fun id -> - id := !id + 1; - Lwt.return - Message. - { - category = Message.Request; - sub_category_opt = None; - id = !id; - sender = !node.address; - recipient; - payload; - }) - -let create_response node request payload = - Message. - { - category = Message.Response; - sub_category_opt = None; - id = request.id; - sender = !node.address; - recipient = request.sender; - payload; - } - -let send_to node message = - let open Message in - let payload = Encoding.pack Message.bin_writer_t message in - let len = Bytes.length payload in - let addr = Address.to_sockaddr message.recipient in - Mutex.unsafe !node.socket (fun socket -> - let%lwt _ = sendto socket payload 0 len [] addr in - Lwt.return ()) - -let recv_next node = - let open Lwt_unix in - let open Util in - (* Peek the first 8 bytes of the incoming datagram - to read the Bin_prot size header. *) - let size_buffer = Bytes.create Encoding.size_header_length in - let%lwt node_socket = Mutex.lock !node.socket in - (* Flag MSG_PEEK means: peeks at an incoming message. - The data is treated as unread and the next recvfrom() - or similar function shall still return this data. - Here, we only need the mg_size. - *) - let%lwt _ = - recvfrom node_socket size_buffer 0 Encoding.size_header_length [MSG_PEEK] - in - let msg_size = - Encoding.read_size_header size_buffer + Encoding.size_header_length in - let msg_buffer = Bytes.create msg_size in - (* Now that we have read the header and the message size, we can read the message *) - let%lwt _ = recvfrom node_socket msg_buffer 0 msg_size [] in - let message = Encoding.unpack Message.bin_read_t msg_buffer in - Mutex.unlock !node.socket; - Lwt.return message - -let request node request recipient = - let%lwt message = create_request node recipient request in - let%lwt () = send_to node message in - let condition_var = Lwt_condition.create () in - Hashtbl.add !node.request_table message.id condition_var; - Lwt_condition.wait condition_var - -let broadcast_request node req recipients = - List.map (request node req) recipients diff --git a/lib/node/dune b/lib/node/dune deleted file mode 100644 index b91407a..0000000 --- a/lib/node/dune +++ /dev/null @@ -1,6 +0,0 @@ -(library - (name node) - (public_name pollinate.node) - (libraries bin_prot lwt lwt.unix pollinate.common) - (preprocess - (pps ppx_bin_prot lwt_ppx))) diff --git a/lib/node/inbox.ml b/lib/node/inbox.ml deleted file mode 100644 index e83e16a..0000000 --- a/lib/node/inbox.ml +++ /dev/null @@ -1,31 +0,0 @@ -open Common -type t = (Message.category, Message.t Tqueue.t) Base.Hashtbl.t - -let create () = - let inbox = Base.Hashtbl.Poly.create () in - let _ = Base.Hashtbl.add inbox ~key:Message.Request ~data:(Tqueue.create ()) in - let _ = - Base.Hashtbl.add inbox ~key:Message.Response ~data:(Tqueue.create ()) in - let _ = - Base.Hashtbl.add inbox ~key:Message.Uncategorized ~data:(Tqueue.create ()) - in - let _ = - Base.Hashtbl.add inbox ~key:Message.Failure_detection - ~data:(Tqueue.create ()) in - inbox - -let find_or_create_category inbox category = - Base.Hashtbl.find_or_add inbox category ~default:(fun () -> Tqueue.create ()) - -let next inbox ?(consume = true) category = - category - |> find_or_create_category inbox - |> if consume then Tqueue.take else Tqueue.peek - -let await_next inbox ?(consume = true) category = - category - |> find_or_create_category inbox - |> if consume then Tqueue.wait_to_take else Tqueue.wait_to_peek - -let push inbox category message = - category |> find_or_create_category inbox |> Tqueue.add message diff --git a/lib/node/inbox.mli b/lib/node/inbox.mli deleted file mode 100644 index a661d49..0000000 --- a/lib/node/inbox.mli +++ /dev/null @@ -1,25 +0,0 @@ -(** A Hashtbl mapping message categories to thread-safe queues -containing messages *) -type t - -(** Initializes an inbox with queues for each type of message. *) -val create : unit -> t - -(** Attempts to retrieve the next message of the given category -from the inbox, and returns None if no message is found. If consume -is true, then the if a message is found it will be removed from the -queue. Otherwise, it will be peeked at but not removed. Creates -a queue for the given category if one has not been created yet. *) -val next : t -> ?consume:bool -> Message.category -> Message.t option Lwt.t - -(** Blocks the current thread of execution until a message of the specified -category is available. If consume is true, the message will be -removed from the queue once it is available. Otherwise, it will be -peeked at. Creates a queue for the given category if one has not - been created yet. *) -val await_next : t -> ?consume:bool -> Message.category -> Message.t Lwt.t - -(** Pushes the given message of the given category -to the corresponding queue. Creates a queue for the given category -if one has not been created yet. *) -val push : t -> Message.category -> Message.t -> unit Lwt.t diff --git a/lib/node/message.ml b/lib/node/message.ml deleted file mode 100644 index 961d686..0000000 --- a/lib/node/message.ml +++ /dev/null @@ -1,20 +0,0 @@ -open Common -open Bin_prot.Std - -type category = - | Uncategorized - | Request - | Response - | Failure_detection - | Custom of string -[@@deriving bin_io] - -type t = { - category : category; - sub_category_opt : (string * string) option; - id : int; - sender : Address.t; - recipient : Address.t; - payload : bytes; -} -[@@deriving bin_io] diff --git a/lib/node/node.mli b/lib/node/node.mli deleted file mode 100644 index 3f425b7..0000000 --- a/lib/node/node.mli +++ /dev/null @@ -1,20 +0,0 @@ -open Common - -module Message = Message -module Client = Client -module Failure_detector = Failure_detector -module Inbox = Inbox - -(** Initializes the node with an initial state, an optional -routing function that the consumer can use to inspect and modify -the incoming message as well as its metadata, and a message -handler that acts on the current state and the Message.t representing -the request. The message handler is used -to initialize a server that runs asynchronously. Returns -a reference to the node. *) -val init : - ?preprocess:(Message.t -> Message.t) -> - msg_handler:(Message.t -> bytes) -> - ?init_peers:Address.t list -> - string * int -> - Types.node ref Lwt.t diff --git a/lib/node/server.ml b/lib/node/server.ml deleted file mode 100644 index e5dcaec..0000000 --- a/lib/node/server.ml +++ /dev/null @@ -1,51 +0,0 @@ -open Common.Util -open Types - -let route node router msg = - let open Message in - let msg = router msg in - Inbox.push !node.inbox msg.category msg - -(** Signals a waiting request with its corresponding response - if it exists. Otherwise returns None. *) -let handle_response request_table res = - let open Message in - let* res in - let* waiting_request = Hashtbl.find_opt request_table res.id in - Some (Lwt_condition.signal waiting_request res) - -(* Sever procedure: - 1. Receive the next incoming message - 2. Route the message - 3. Grab the next response if it exists and send it to the request waiting for it - 4. Grab the next request if it exists and send it to the message handler along with the - node's state - 5. Send the encoded response from the message handler to the requester *) -let run node router msg_handler = - let rec server () = - let%lwt message = Client.recv_next node in - let%lwt () = route node router message in - - let%lwt () = - match%lwt Inbox.next !node.inbox Message.Failure_detection with - | Some message -> Failure_detector.handle_message node message - | None -> Lwt.return () in - - let%lwt () = Failure_detector.suspicion_detection node in - let%lwt () = Failure_detector.failure_detection node in - - let%lwt next_response = Inbox.next !node.inbox Message.Response in - let _ = handle_response !node.request_table next_response in - - let%lwt request = Inbox.next !node.inbox Message.Request in - let%lwt () = - match request with - | Some request -> - let response = - request |> msg_handler |> Client.create_response node request in - let%lwt () = Client.send_to node response in - Lwt.return () - | None -> Lwt.return () in - - server () in - Lwt.async server diff --git a/lib/pnode/client.ml b/lib/pnode/client.ml new file mode 100644 index 0000000..36065db --- /dev/null +++ b/lib/pnode/client.ml @@ -0,0 +1,68 @@ +open Common +open Types + +let address_of { address; _ } = address + +let peer_from { address; peers; _ } = + Peer. + { + address; + status = Alive; + last_suspicious_status = None; + neighbors = peers; + } + +let add_peer node (peer : Peer.t) = + Base.Hashtbl.add node.peers ~key:peer.address ~data:peer + +let create_request node recipient (payload, payload_signature) = + Mutex.with_lock !node.current_request_id (fun id -> + id := !id + 1; + Lwt.return + Message. + { + category = Message.Request; + sub_category_opt = None; + id = !id; + timestamp = Unix.gettimeofday (); + sender = !node.address; + recipients = [recipient]; + payload; + payload_signature; + }) + +let create_response node request (payload, payload_signature) = + Message. + { + category = Message.Response; + sub_category_opt = None; + id = request.id; + timestamp = Unix.gettimeofday (); + sender = !node.address; + recipients = [request.sender]; + payload; + payload_signature; + } + +let create_post node (payload, payload_signature) = + Message. + { + category = Message.Post; + id = -1; + sub_category_opt = None; + timestamp = Unix.gettimeofday (); + sender = !node.address; + recipients = []; + payload; + payload_signature; + } + +let request node request recipient = + let%lwt message = create_request node recipient request in + let%lwt () = Networking.send_to node message in + let condition_var = Lwt_condition.create () in + Hashtbl.add !node.request_table message.id condition_var; + Lwt_condition.wait condition_var + +let post node message = + !node.disseminator <- Disseminator.post !node.disseminator message diff --git a/lib/node/client.mli b/lib/pnode/client.mli similarity index 50% rename from lib/node/client.mli rename to lib/pnode/client.mli index 1068e2d..9292de2 100644 --- a/lib/node/client.mli +++ b/lib/pnode/client.mli @@ -11,32 +11,26 @@ val address_of : node -> Address.t (** Constructs a [Peer.t] from a [Types.node]. *) val peer_from : node -> Peer.t -(** Add a peer to the know peers. *) +(** Add a peer to the known peers. *) val add_peer : node -> Peer.t -> [`Duplicate | `Ok] +(** Begins disseminating an encoded message meant to be witnessed by as many + nodes in the network as possible. *) +val post : node ref -> Message.t -> unit + (** [create_request node recipient payload] creates a [Message.t] of the {i Request category} addressed to {i recipient} containing {i payload}. *) -val create_request : node ref -> Address.t -> bytes -> Message.t Lwt.t +val create_request : + node ref -> Address.t -> bytes * bytes option -> Message.t Lwt.t (** [create_response node request payload] creates a [Message.t] of the {i Response category} that responds to {i request} whose content is {i payload}. *) -val create_response : node ref -> Message.t -> bytes -> Message.t - -(** Sends a message via datagram from the given [Types.node] -to a specified peer within the [Message.t]. Construct a message with one of the -[create_*] functions to then feed to this function. *) -val send_to : node ref -> Message.t -> unit Lwt.t - -(** Waits for the next incoming message and returns it. *) -val recv_next : node ref -> Message.t Lwt.t +val create_response : node ref -> Message.t -> bytes * bytes option -> Message.t (** Sends an encoded {i request} to the specified peer and returns a promise holding the response from the peer. This function blocks the current thread of execution until a response arrives. *) -val request : node ref -> bytes -> Address.t -> Message.t Lwt.t +val request : node ref -> bytes * bytes option -> Address.t -> Message.t Lwt.t -(** Broadcasts a request containing the given payload to a list -of recipients and collects the responses in a list of [Message.t Lwt.t]. *) -val broadcast_request : - node ref -> bytes -> Address.t list -> Message.t Lwt.t list +val create_post : node ref -> bytes * bytes option -> Message.t diff --git a/lib/pnode/disseminator.ml b/lib/pnode/disseminator.ml new file mode 100644 index 0000000..9eb9152 --- /dev/null +++ b/lib/pnode/disseminator.ml @@ -0,0 +1,66 @@ +type pool_elt = { + message : Message.t; + remaining : int; +} + +(** Set of md5 message hashes in hex-string form + for storing "seen" messages *) +module DigestSet = Set.Make (Digest) + +type t = { + round : int; + pool : pool_elt list; + num_rounds : int; + epoch_length : float; + seen : DigestSet.t; +} + +let create ~num_rounds ~epoch_length = + { round = 0; pool = []; num_rounds; epoch_length; seen = DigestSet.empty } + +(* Increments disseminator.round and decrements pool_elt.remaining for + each disseminator pool element. Removes messages from the pool + that have been disseminated num_rounds times or which are + older than the epoch length.*) +let next_round disseminator = + let round = disseminator.round + 1 in + let pool = + disseminator.pool + |> List.map (fun ({ remaining; _ } as elt) -> + { elt with remaining = remaining - 1 }) + |> List.filter (fun elt -> + elt.remaining > 0 + && elt.message.timestamp > Unix.time () -. disseminator.epoch_length) + in + + { disseminator with round; pool } + +let post disseminator message = + let open Message in + let time = Unix.time () in + if message.timestamp > time -. disseminator.epoch_length then + let pool = + { message; remaining = disseminator.num_rounds } :: disseminator.pool + in + let digest_of_post = Message.hash_of message in + let seen = DigestSet.add digest_of_post disseminator.seen in + { disseminator with pool; seen } + else + disseminator + +let broadcast_queue disseminator = + List.map (fun e -> e.message) disseminator.pool + +let seen disseminator message = + let open Message in + let time = Unix.time () in + if message.timestamp > time -. disseminator.epoch_length then + let hash = Message.hash_of message in + DigestSet.mem hash disseminator.seen + else + false + +let get_seen_messages disseminator = + disseminator.seen |> DigestSet.to_seq |> List.of_seq + +let current_round { round; _ } = round diff --git a/lib/pnode/disseminator.mli b/lib/pnode/disseminator.mli new file mode 100644 index 0000000..bcea15e --- /dev/null +++ b/lib/pnode/disseminator.mli @@ -0,0 +1,44 @@ +(** Component responsible for gossip-style dissemination of + messages across the network *) + +(** A record containing information and state relevant to the + dissemination component. *) +type t + +(** Creates a dissemination component that can be attached to a node + when given the number of "rounds" for which each new message should + be disseminated and an "epoch length", in seconds, which determines + whether a message is too old to be disseminated again by checking + whether the message is newer than n seconds old, where n is the + given epoch length. *) +val create : num_rounds:int -> epoch_length:float -> t + +(** Starts the next round of dissemination, affecting the state + of the disseminator. In particular, this function causes the + current round to increase, reduces the number of rounds remaining + for each message being disseminated, and filters out messages with + no rounds remaining or a timestamp that's older than epoch_length + seconds. *) +val next_round : t -> t + +(** Adds a new message to the dissemination pool. The message will not be + posted if it is older than the epoch_length. Otherwise, the message + will begin to be disseminated automatically as long as the disseminator + is running along with Networking.disseminate.*) +val post : t -> Message.t -> t + +(** Returns the list of messages that need to be disseminated. For + exclusive use by Networking.disseminate. *) +val broadcast_queue : t -> Message.t list + +(** Determines whether the dissemination component has witnessed a + given message before, or whether the message is too old to be + retained in the set of seen messages. *) +val seen : t -> Message.t -> bool + +(** Returns the 7 digit hashes of all the messages + that the disseminator has seen. *) +val get_seen_messages : t -> string list + +(** Returns the current disseminator round. *) +val current_round : t -> int diff --git a/lib/pnode/dune b/lib/pnode/dune new file mode 100644 index 0000000..ed5e578 --- /dev/null +++ b/lib/pnode/dune @@ -0,0 +1,6 @@ +(library + (name pnode) + (public_name pollinate.pnode) + (libraries bin_prot lwt lwt.unix pollinate.common) + (preprocess + (pps ppx_bin_prot ppx_deriving.show lwt_ppx))) diff --git a/lib/node/failure_detector.ml b/lib/pnode/failure_detector.ml similarity index 80% rename from lib/node/failure_detector.ml rename to lib/pnode/failure_detector.ml index 0db1061..4ce1d07 100644 --- a/lib/node/failure_detector.ml +++ b/lib/pnode/failure_detector.ml @@ -34,34 +34,6 @@ let wait_ack_timeout t sequence_number timeout = Lwt.return @@ Result.Ok "Successfully received acknowledge"); ] -(** Basic random shuffle, see https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle*) -let knuth_shuffle known_peers = - let shuffled_array = Array.copy (Array.of_list known_peers) in - let initial_array_length = Array.length shuffled_array in - for i = initial_array_length - 1 downto 1 do - let k = Random.int (i + 1) in - let x = shuffled_array.(k) in - shuffled_array.(k) <- shuffled_array.(i); - shuffled_array.(i) <- x - done; - Array.to_list shuffled_array - -(* Regarding the SWIM protocol, if peer A cannot get ACK from peer B (timeout): - A sets B as `suspicious` - A randomly picks one (or several, should it also be randomly determined?) peer(s) from its list - and ask him/them to ping B.*) - -(** This function return the random peer, to which we will ask to ping the first peer *) -let rec pick_random_neighbors neighbors number_of_neighbors = - let addresses = neighbors |> Base.Hashtbl.keys |> knuth_shuffle in - match addresses with - | [] -> failwith "pick_random_peers" - | elem :: _ -> - if number_of_neighbors = 1 then - [elem] - else - elem :: pick_random_neighbors neighbors (number_of_neighbors - 1) - (** Updates a peer in the node's peer list with the given status. Returns a result that contains unit if the peer is found in the node's list, and @@ -82,20 +54,22 @@ let update_peer_status node peer status = (Printf.sprintf "Failed to find peer with address %s:%d in node peer list" peer.address.address peer.address.port) -let create_message node message (recipient : Peer.t) = +let create_message node message recipient = Message. { category = Failure_detection; sub_category_opt = None; id = -1; + timestamp = Unix.gettimeofday (); sender = Client.address_of !node; - recipient = recipient.address; + recipients = [recipient.Peer.address]; payload = Encoding.pack bin_writer_message message; + payload_signature = None; } -let send_message message node (recipient : Peer.t) = +let send_message message node recipient = let message = create_message node message recipient in - Client.send_to node message + Networking.send_to node message let send_ping_to node peer = send_message Ping node peer @@ -149,7 +123,7 @@ let probe_peer t node peer_to_update = | Error _ -> ( let pingers = t.config.helpers_size - |> pick_random_neighbors !node.peers + |> Networking.pick_random_neighbors !node.peers |> List.map Peer.from in let _ = List.map (send_ping_request_to node) pingers in let wait_time = t.config.protocol_period - t.config.round_trip_time in @@ -172,8 +146,7 @@ let suspicion_detection node = | 0 -> Lwt.return () | _ -> let random_peer = - List.map (fun p -> p.address) available_peers |> knuth_shuffle |> List.hd - in + Networking.pick_random_neighbors !node.peers 1 |> List.hd in let _ = Lwt.join [ diff --git a/lib/node/failure_detector.mli b/lib/pnode/failure_detector.mli similarity index 92% rename from lib/node/failure_detector.mli rename to lib/pnode/failure_detector.mli index 3ef3e48..5d1914a 100644 --- a/lib/node/failure_detector.mli +++ b/lib/pnode/failure_detector.mli @@ -40,10 +40,6 @@ val failure_detection : node ref -> unit Lwt.t (**/**) -val knuth_shuffle : Peer.t list -> Peer.t list - -val pick_random_neighbors : ('a, 'b) Base.Hashtbl.t -> int -> 'a list - val update_peer_status : Types.node ref -> Common.Peer.t -> Common.Peer.status -> (unit, string) result diff --git a/lib/pnode/message.ml b/lib/pnode/message.ml new file mode 100644 index 0000000..4fbb782 --- /dev/null +++ b/lib/pnode/message.ml @@ -0,0 +1,35 @@ +open Common +open Bin_prot.Std + +type category = + | Uncategorized + | Request + | Response + | Post + | Failure_detection + | Custom of string +[@@deriving bin_io, show] + +type t = { + category : category; + sub_category_opt : (string * string) option; + id : int; + timestamp : float; + sender : Address.t; + recipients : Address.t list; + payload : bytes; + payload_signature : bytes option; +} +[@@deriving bin_io] + +let hash_of m = + [ + m.sender.address; + string_of_int m.sender.port; + string_of_float m.timestamp; + Bytes.to_string m.payload; + ] + |> String.concat "" + |> Digest.string + |> Digest.to_hex + |> fun s -> String.sub s 0 7 diff --git a/lib/node/message.mli b/lib/pnode/message.mli similarity index 70% rename from lib/node/message.mli rename to lib/pnode/message.mli index 4068b67..50fbeb9 100644 --- a/lib/node/message.mli +++ b/lib/pnode/message.mli @@ -1,6 +1,6 @@ (** Messages received by the [node], whether they are requests, responses, or protocol-specific messages. For consumer use -only when implementing a routing function for the +only when implementing a preprocessing function for the node. *) open Common @@ -14,18 +14,23 @@ type category = | Uncategorized | Request | Response + | Post | Failure_detection | Custom of string -[@@deriving bin_io] +[@@deriving bin_io, show] (** Messages received from [peers] which are -stored in the node's inbox. *) +processed by the node's message handler. *) type t = { category : category; sub_category_opt : (string * string) option; id : int; + timestamp : float; sender : Address.t; - recipient : Address.t; + recipients : Address.t list; payload : bytes; + payload_signature : bytes option; } [@@deriving bin_io] + +val hash_of : t -> Digest.t diff --git a/lib/pnode/networking.ml b/lib/pnode/networking.ml new file mode 100644 index 0000000..7937b2d --- /dev/null +++ b/lib/pnode/networking.ml @@ -0,0 +1,91 @@ +open Common +open Common.Util +open Types +open Lwt_unix + +let send_to node message = + let open Message in + (* let%lwt () = log node "Sending message\n" in *) + let payload = Encoding.pack Message.bin_writer_t message in + let len = Bytes.length payload in + let addrs = List.map Address.to_sockaddr message.recipients in + Mutex.unsafe !node.socket (fun socket -> + let%lwt _ = + Lwt_list.map_p (fun addr -> sendto socket payload 0 len [] addr) addrs + in + Lwt.return ()) + +let recv_next node = + let open Lwt_unix in + let open Util in + (* Peek the first 8 bytes of the incoming datagram + to read the Bin_prot size header. *) + let size_buffer = Bytes.create Encoding.size_header_length in + let%lwt node_socket = Mutex.lock !node.socket in + (* Flag MSG_PEEK means: peeks at an incoming message. + The data is treated as unread and the next recvfrom() + or similar function shall still return this data. + Here, we only need the mg_size. + *) + let%lwt _ = + recvfrom node_socket size_buffer 0 Encoding.size_header_length [MSG_PEEK] + in + let msg_size = + Encoding.read_size_header size_buffer + Encoding.size_header_length in + let msg_buffer = Bytes.create msg_size in + (* Now that we have read the header and the message size, we can read the message *) + let%lwt _ = recvfrom node_socket msg_buffer 0 msg_size [] in + let message = Encoding.unpack Message.bin_read_t msg_buffer in + Mutex.unlock !node.socket; + Lwt.return message + +(** Basic random shuffle, see https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle*) +let knuth_shuffle known_peers = + let shuffled_array = Array.copy (Array.of_list known_peers) in + let initial_array_length = Array.length shuffled_array in + for i = initial_array_length - 1 downto 1 do + let k = Random.int (i + 1) in + let x = shuffled_array.(k) in + shuffled_array.(k) <- shuffled_array.(i); + shuffled_array.(i) <- x + done; + Array.to_list shuffled_array + +(** This function return the random peer, to which we will ask to ping the first peer *) +let pick_random_neighbors neighbors number_of_neighbors = + let rec take n l = + match l with + | [] -> [] + | h :: t when n > 0 -> h :: take (n - 1) t + | _ -> [] in + neighbors |> Base.Hashtbl.keys |> knuth_shuffle |> take number_of_neighbors + +(** Injects the list of recipients into the message and sends it to + each recipient with a log message. *) +let broadcast node message (recipients : Address.t list) = + (* let%lwt () = + recipients + |> List.map (fun Address.{ port; _ } -> string_of_int port) + |> String.concat " ; " + |> Printf.sprintf "Disseminating post %s from author %d to peers: [%s]\n" + (Message.hash_of message) message.sender.port + |> log node in *) + let message = Message.{ message with recipients } in + let%lwt () = send_to node message in + Lwt.return () + +(** Picks random peers to broadcast each message in the dissemination + queue to, then sends them. This function progresses the + disseminator to the next round, so no other function should + do this. *) +let disseminate node = + let dissemination_group = pick_random_neighbors !node.peers 2 in + let _ = + Disseminator.broadcast_queue !node.disseminator + |> List.map (fun message -> broadcast node message dissemination_group) + in + Lwt.return (!node.disseminator <- Disseminator.next_round !node.disseminator) + +module Testing = struct + let knuth_shuffle = knuth_shuffle +end diff --git a/lib/pnode/networking.mli b/lib/pnode/networking.mli new file mode 100644 index 0000000..e818c96 --- /dev/null +++ b/lib/pnode/networking.mli @@ -0,0 +1,23 @@ +open Types +open Common + +(** Sends a message via datagram from the given [Types.node] +to a specified peer within the [Message.t]. Construct a message with one of the +[create_*] functions to then feed to this function. *) +val send_to : node ref -> Message.t -> unit Lwt.t + +(** Waits for the next incoming message and returns it. *) +val recv_next : node ref -> Message.t Lwt.t + +(** Advances a node's disseminator by disseminating the messages in the queue + and pruning outdated messages from the queue. *) +val disseminate : node ref -> unit Lwt.t + +(** Given a Base.Hashtbl of Addresses to Peers and a number n of peers to be + randomly chosen, returns a list of addresses corresponding to n *) +val pick_random_neighbors : + (Address.t, Peer.t) Base.Hashtbl.t -> int -> Address.t list + +module Testing : sig + val knuth_shuffle : 'a list -> 'a list +end diff --git a/lib/node/node.ml b/lib/pnode/pnode.ml similarity index 53% rename from lib/node/node.ml rename to lib/pnode/pnode.ml index ad939c0..7848165 100644 --- a/lib/node/node.ml +++ b/lib/pnode/pnode.ml @@ -3,12 +3,12 @@ open Types module Message = Message module Client = Client -module Failure_detector = Failure_detector -module Inbox = Inbox -let init ?(preprocess = fun m -> m) ~msg_handler ?(init_peers = []) - (address, port) = +type t = Types.node + +let init ?(init_peers = []) Address.{ address; port } = let open Util in + (* Printf.printf "Opening port: %d\n%!" port; *) let%lwt socket = Net.create_socket port in let peers = Base.Hashtbl.create ~growth_allowed:true ~size:0 (module Address) in @@ -23,7 +23,6 @@ let init ?(preprocess = fun m -> m) ~msg_handler ?(init_peers = []) current_request_id = Mutex.create (ref 0); request_table = Hashtbl.create 20; socket = Mutex.create socket; - inbox = Inbox.create (); failure_detector = Failure_detector.make { @@ -33,6 +32,19 @@ let init ?(preprocess = fun m -> m) ~msg_handler ?(init_peers = []) helpers_size = 3; }; peers; + disseminator = Disseminator.create ~num_rounds:10 ~epoch_length:50.; } in - Server.run node preprocess msg_handler; Lwt.return node + +let run_server ?(preprocessor = fun m -> m) ~msg_handler node = + (* let _ = Printf.sprintf "I am inside run_server\n%!" in *) + Server.run node preprocessor msg_handler + +let seen node message = Disseminator.seen !node.disseminator message + +module Testing = struct + module Failure_detector = Failure_detector + module Networking = Networking + let broadcast_queue node = Disseminator.broadcast_queue !node.disseminator + let disseminator_round node = Disseminator.current_round !node.disseminator +end diff --git a/lib/pnode/pnode.mli b/lib/pnode/pnode.mli new file mode 100644 index 0000000..a035e0c --- /dev/null +++ b/lib/pnode/pnode.mli @@ -0,0 +1,29 @@ +open Common + +module Message = Message +module Client = Client + +type t = Types.node + +(** Initializes the node with an initial state, an optional +preprocessing function that the consumer can use to inspect and modify +the incoming message as well as its metadata, and a message +handler that acts on the current state and the incoming Message.t. +The message handler is used to initialize a server that runs asynchronously. +Returns reference to the newly created node. *) +val init : ?init_peers:Address.t list -> Address.t -> t ref Lwt.t + +val run_server : + ?preprocessor:(Message.t -> Message.t) -> + msg_handler:(Message.t -> bytes option * bytes option) -> + t ref -> + 'b Lwt.t + +val seen : t ref -> Message.t -> bool + +module Testing : sig + module Failure_detector = Failure_detector + module Networking = Networking + val broadcast_queue : t ref -> Message.t list + val disseminator_round : t ref -> int +end diff --git a/lib/pnode/server.ml b/lib/pnode/server.ml new file mode 100644 index 0000000..edb6ef5 --- /dev/null +++ b/lib/pnode/server.ml @@ -0,0 +1,130 @@ +open Util +open Common +open Types + +(** Signals a waiting request with its corresponding response + if it exists. Otherwise returns None. *) +let handle_response node res = + let open Message in + match Hashtbl.find_opt !node.request_table res.id with + | Some waiting_request -> Lwt_condition.signal waiting_request res + | None -> () + +(* Preprocess a message, log some information about it, then handle it + based on its category. The "rules" are as follows: + + Response: send the message to a "handle_response" function which wakes + up a sleeping request function with the response it was waiting for. + + Request: run the message handler on the incoming request and, if the message + handler returned a response, send it to the requester. + + Failure_detection: send the message to the Failure_detector.handle_message function + + Post: check if the post has been seen (or if its outdated). If not, then handle the post with + the message handler, then disseminate it to this node's peers by reposting it. + + Otherwise, we just apply the message handler and that's it. +*) +let process_message node preprocessor msg_handler = + (* let _ = Printf.sprintf "I am inside process_message\n%!" in *) + let open Message in + let%lwt message = Networking.recv_next node in + let message = preprocessor message in + (* let%lwt () = + log node + (Printf.sprintf "Processing message %s from %d...\n" + (Message.hash_of message) message.sender.port) in *) + let%lwt () = + match message.category with + | Response -> Lwt.return (handle_response node message) + | Request -> ( + (* let%lwt () = + log node + (Printf.sprintf "%s:%d : Processing request from %s:%d\n" + !node.address.address !node.address.port message.sender.address + message.sender.port) in *) + match msg_handler message with + | Some payload, payload_signature -> + (* let _ = Printf.sprintf "I am inside msg_handler, found payload\n%!" in *) + (payload, payload_signature) + |> Client.create_response node message + |> Networking.send_to node + | None, _ -> + (* let _ = Printf.sprintf "I am inside msg_handler, missing payload\n%!" in *) + Lwt.return ()) + | Failure_detection -> Failure_detector.handle_message node message + | Post -> + if not (Disseminator.seen !node.disseminator message) then ( + (* let%lwt () = + log node + (Printf.sprintf "%s:%d : Processing post %s from %s:%d\n" + !node.address.address !node.address.port + (Message.hash_of message) message.sender.address + message.sender.port) in *) + let _ = msg_handler message in + (* let%lwt () = log node "Adding message to broadcast queue\n" in *) + Client.post node message; + Lwt.return ()) + else + (* log node + (Printf.sprintf "Got post %s from %s:%d but saw it already\n" + (Message.hash_of message) message.sender.address + message.sender.port) *) + Lwt.return () + | _ -> + let _ = msg_handler message in + Lwt.return () in + Lwt.return () + +(** Log some initial information at the beginning of a server iteration. + See comments for descriptions regarding what is actually being logged. *) +let _print_logs node = + (* Check that the server is in fact running *) + let%lwt () = log node "Running server\n" in + (* Check which posts the node has seen so far *) + let%lwt () = + log node + (Printf.sprintf "Seen: %s\n" + (Disseminator.get_seen_messages !node.disseminator + |> String.concat " ; ")) in + (* Check who the current peers of the node are *) + let%lwt () = + !node.peers + |> Base.Hashtbl.keys + |> List.map (fun Address.{ port; _ } -> string_of_int port) + |> String.concat " ; " + |> Printf.sprintf "Peers: %s\n" + |> log node in + (* Check which posts are currently being disseminated by the node *) + if List.length (Disseminator.broadcast_queue !node.disseminator) > 0 then + let%lwt () = + !node.disseminator + |> Disseminator.broadcast_queue + |> List.map Message.hash_of + |> String.concat " " + |> Printf.sprintf "Broadcast Queue: %s\n" + |> log node in + Lwt.return () + else + Lwt.return () + +(* Sever procedure: + 0. Log pertinent information about the current node. + 1. Start a new thread for handling any incoming message. + 2. Run the failure detector. + 3. Run the disseminator, this includes actually sending messages to be + disseminated across the network. + 4. Wait 0.001 seconds before restarting the procedure. *) +let rec run node preprocessor msg_handler = + (* Step 0 *) + (* let%lwt () = print_logs node in *) + (* Step 1 *) + let _ = process_message node preprocessor msg_handler in + (* Step 2 *) + let%lwt () = Failure_detector.failure_detection node in + (* Step 3 *) + let%lwt () = Networking.disseminate node in + (* Step 4 *) + let%lwt () = Lwt_unix.sleep 0.001 in + run node preprocessor msg_handler diff --git a/lib/pnode/server.mli b/lib/pnode/server.mli new file mode 100644 index 0000000..17019e0 --- /dev/null +++ b/lib/pnode/server.mli @@ -0,0 +1,8 @@ +(** Runs the server given a reference to a node, a message preprocessor and a message handler. + The server is responsible for running failure detection and dissemination processes, as well + as issuing responses to nodes making individual requests via the message handler. *) +val run : + Types.node ref -> + (Message.t -> Message.t) -> + (Message.t -> bytes option * bytes option) -> + 'b Lwt.t diff --git a/lib/node/types.ml b/lib/pnode/types.ml similarity index 94% rename from lib/node/types.ml rename to lib/pnode/types.ml index 934844c..b69ef6b 100644 --- a/lib/node/types.ml +++ b/lib/pnode/types.ml @@ -20,7 +20,7 @@ type node = { current_request_id : int ref Mutex.t; request_table : (int, Message.t Lwt_condition.t) Hashtbl.t; socket : file_descr Mutex.t; - inbox : Inbox.t; failure_detector : failure_detector; peers : (Address.t, Peer.t) Base.Hashtbl.t; + mutable disseminator : Disseminator.t; } diff --git a/lib/node/types.mli b/lib/pnode/types.mli similarity index 65% rename from lib/node/types.mli rename to lib/pnode/types.mli index 2316105..cd3c30a 100644 --- a/lib/node/types.mli +++ b/lib/pnode/types.mli @@ -39,21 +39,26 @@ type failure_detector = { (** Represents a node with some state in a peer-to-peer network *) type node = { address : Address.t; - (** An ID that is incremented whenever a request is - made from this node. The response matching this - request will carry the same ID, allowing the response - to be identified and thus stopping the request from - blocking. *) + (* An ID that is incremented whenever a request is + made from this node. The response matching this + request will carry the same ID, allowing the response + to be identified and thus stopping the request from + blocking. *) current_request_id : int ref Mutex.t; - (** A hashtable that pairs request IDs with condition variables. - When a response is received by the server, it checks this table - for a waiting request and signals the request's condition variable - with the incoming response. *) + (* A hashtable that pairs request IDs with condition variables. + When a response is received by the server, it checks this table + for a waiting request and signals the request's condition variable + with the incoming response. *) request_table : (int, Message.t Lwt_condition.t) Hashtbl.t; socket : file_descr Mutex.t; - (** A store of incoming messages for the node. Stores - messages separately by category. *) - inbox : Inbox.t; + (* Failure detection component ; runs automatically with the server and is responsible + for automatically removing dead nodes from the peers table. *) failure_detector : failure_detector; + (* Hashtable mapping addresses to Peers with statuses according + to the SWIM failure-detection protocol *) peers : (Address.t, Peer.t) Base.Hashtbl.t; + (* Dissemination component ; runs automatically with the server and is responsible + for automatically disseminating both new Post messages and received Post messages + with other nodes in the network *) + mutable disseminator : Disseminator.t; } diff --git a/lib/pnode/util.ml b/lib/pnode/util.ml new file mode 100644 index 0000000..7ef2f62 --- /dev/null +++ b/lib/pnode/util.ml @@ -0,0 +1,11 @@ +open Types + +(** Prints a log message with information about the node it pertains to and the current time. *) +let log node _msg = + let _current_time = + Unix.time () |> Unix.localtime |> fun tm -> + Printf.sprintf "%02d:%02d:%02d" tm.Unix.tm_hour tm.Unix.tm_min + tm.Unix.tm_sec in + let _addr = Printf.sprintf "%s:%d" !node.address.address !node.address.port in + Lwt.return () + (* Lwt_io.printf "[%s @ %s] %s" addr current_time msg *) diff --git a/lib/pollinate.ml b/lib/pollinate.ml index 84d1900..ab4d85a 100644 --- a/lib/pollinate.ml +++ b/lib/pollinate.ml @@ -1,4 +1,4 @@ module Address = Common.Address module Util = Common.Util module Peer = Common.Peer -module Node = Node +module PNode = Pnode diff --git a/test/commons.ml b/test/commons.ml index 887ebb3..4c9d7fa 100644 --- a/test/commons.ml +++ b/test/commons.ml @@ -1,10 +1,10 @@ (** Utils function shared by the different tests modules *) module Commons = struct - open Pollinate.Node + open Pollinate.PNode open Pollinate.Util open Messages - let preprocess msg = + let preprocessor msg = let open Messages in match msg.Message.category with | Request -> @@ -17,14 +17,18 @@ module Commons = struct { msg with payload = Encoding.pack bin_writer_response r } | _ -> msg - let msg_handler request = + let msg_handler message = let open Messages in let open Message in - let request = Encoding.unpack bin_read_request request.payload in - let response = - match request with - | Ping -> Pong - | Get -> Success "Ok" - | Insert _ -> Success "Successfully added value to state" in - Encoding.pack bin_writer_message (Response response) + match message.category with + | Request -> + let request = Encoding.unpack bin_read_request message.payload in + let response = + match request with + | Ping -> Pong + | Get -> Pong + | Insert _ -> Success "Successfully added value to state" in + ( Response response |> Encoding.pack bin_writer_message |> Option.some, + None ) + | _ -> (None, None) end diff --git a/test/disseminator_tests.ml b/test/disseminator_tests.ml new file mode 100644 index 0000000..a7b275e --- /dev/null +++ b/test/disseminator_tests.ml @@ -0,0 +1,105 @@ +open Lwt.Infix +open Commons +open Pollinate +open Pollinate.PNode + +module Disseminator_tests = struct + let node = + Lwt_main.run + (let%lwt node_a = + PNode.init ~init_peers:[] + Address.{ address = "127.0.0.1"; port = 5000 } in + Lwt.return node_a) + + let queue_insertion_test () = + let _server = PNode.run_server ~msg_handler:Commons.msg_handler node in + let payload = + Client.address_of !node + |> (fun Address.{ port; _ } -> port) + |> string_of_int + |> String.to_bytes in + Client.create_post node (payload, None) |> Client.post node; + + Lwt.return (List.length (PNode.Testing.broadcast_queue node)) + + let queue_removal_test () = + let _server = PNode.run_server ~msg_handler:Commons.msg_handler node in + let payload = + Client.address_of !node + |> (fun Address.{ port; _ } -> port) + |> string_of_int + |> String.to_bytes in + Client.create_post node (payload, None) |> Client.post node; + + let%lwt () = + while%lwt PNode.Testing.disseminator_round node <= 10 do + Lwt_unix.sleep 0.1 + done in + Lwt.return (List.length (PNode.Testing.broadcast_queue node)) + + let seen_message_test () = + let _server = PNode.run_server ~msg_handler:Commons.msg_handler node in + let payload = + Client.address_of !node + |> (fun Address.{ port; _ } -> port) + |> string_of_int + |> String.to_bytes in + let message = Client.create_post node (payload, None) in + message |> Client.post node; + + Lwt.return (PNode.seen node message) +end + +(** Test for dissemination given a specific Pnode. *) +let test_queue_removal _ () = + Disseminator_tests.queue_removal_test () + >|= Alcotest.(check int) + "Length of broadcast queue is 0 10 rounds after the client posts" 0 + +let test_queue_insertion _ () = + Disseminator_tests.queue_insertion_test () + >|= Alcotest.(check int) + "Length of broadcast queue is 1 after the client posts" 1 + +let test_seen_message _ () = + Disseminator_tests.seen_message_test () + >|= Alcotest.(check bool) + "A message that's just been posted is seen by the disseminator" true + +let () = + Lwt_main.run + @@ Alcotest_lwt.run "Disseminator tests" + [ + ( "disseminator functions", + [ + Alcotest_lwt.test_case + "Messages are removed from queue after 10 rounds" `Quick + test_queue_removal; + Alcotest_lwt.test_case + "Messages are added to the queue when posted" `Quick + test_queue_insertion; + Alcotest_lwt.test_case + "Messages that are posted are immediately seen" `Quick + test_seen_message + (* Alcotest_lwt.test_case "Dissemination from A" `Quick + (test_disseminate_from Gossip_tests.node_a); *) + (* Alcotest_lwt.test_case "Dissemination from B" `Quick + (test_disseminate_from Gossip_tests.node_b); + Alcotest_lwt.test_case "Dissemination from C" `Quick + (test_disseminate_from Gossip_tests.node_c); + Alcotest_lwt.test_case "Dissemination from D" `Quick + (test_disseminate_from Gossip_tests.node_d); *) + (* Alcotest_lwt.test_case "Dissemination from E" `Quick + (test_disseminate_from Gossip_tests.node_e); *) + (* Alcotest_lwt.test_case "Dissemination from F" `Quick + (test_disseminate_from Gossip_tests.node_f); *) + (* Alcotest_lwt.test_case "Dissemination from G" `Quick + (test_disseminate_from Gossip_tests.node_g); *) + (* Alcotest_lwt.test_case "Dissemination from H" `Quick + (test_disseminate_from Gossip_tests.node_h); *) + (* Alcotest_lwt.test_case "Dissemination from I" `Quick + (test_disseminate_from Gossip_tests.node_i); *) + (* Alcotest_lwt.test_case "Dissemination from J" `Quick + (test_disseminate_from Gossip_tests.node_j); ;*); + ] ); + ] diff --git a/test/dune b/test/dune index 97f6d6d..ed3de82 100644 --- a/test/dune +++ b/test/dune @@ -1,19 +1,25 @@ (tests (names node_tests + gossip_tests + disseminator_tests peer_prop address_prop util_prop + networking_prop failure_detector_prop failure_detector_tests) (modules node_tests + gossip_tests + disseminator_tests messages generators peer_prop address_prop commons util_prop + networking_prop failure_detector_prop failure_detector_tests) (libraries pollinate bin_prot lwt alcotest-lwt qcheck-core qcheck-alcotest) diff --git a/test/failure_detector_prop.ml b/test/failure_detector_prop.ml index fe0177b..f7fc885 100644 --- a/test/failure_detector_prop.ml +++ b/test/failure_detector_prop.ml @@ -1,43 +1,22 @@ open QCheck2.Gen open Pollinate.Peer -open Commons - -module SUT = Pollinate.Node.Failure_detector +open Pollinate +module SUT = Pollinate.PNode.Testing.Failure_detector let node_a = - Lwt_main.run - (Node.init ~preprocess:Commons.preprocess ~msg_handler:Commons.msg_handler - ("127.0.0.1", 3002)) - -let knuth_shuffle_size = - QCheck2.Test.make ~count:1000 - ~name:"Knuth_shuffle does not change the size of the list" - (QCheck2.Gen.list Generators.peer_gen) (fun peers -> - List.length (SUT.knuth_shuffle peers) == List.length peers) + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 3002 }) let update_peer = QCheck2.Test.make ~count:1000 ~name:"update_neighbor_status successfully update neighbor status" (pair Generators.peer_gen Generators.peer_status_gen) (fun (neighbor, neighbor_status) -> - let _ = add_neighbor (Pollinate.Node.Client.peer_from !node_a) neighbor in + let _ = add_neighbor (Pollinate.PNode.Client.peer_from !node_a) neighbor in let _ = SUT.update_peer_status node_a neighbor neighbor_status in neighbor.status = neighbor_status) -let pick_random_neighbors = - QCheck2.Test.make ~count:1000 - ~name: - "pick_random_neighbors on a peer with a single neighbor, returns this \ - neighbors" (pair Generators.peer_gen Generators.peer_gen) - (fun (peer, neighbor) -> - let _ = add_neighbor peer neighbor in - let random_neighbor = - List.hd @@ SUT.pick_random_neighbors peer.neighbors 1 in - random_neighbor == neighbor.address) - let () = let failure_detector_prop = - List.map QCheck_alcotest.to_alcotest - [knuth_shuffle_size; update_peer; pick_random_neighbors] in + List.map QCheck_alcotest.to_alcotest [update_peer] in Alcotest.run "Failure detector" [("failure_detector.ml", failure_detector_prop)] diff --git a/test/failure_detector_tests.ml b/test/failure_detector_tests.ml index bc82743..3fc2b95 100644 --- a/test/failure_detector_tests.ml +++ b/test/failure_detector_tests.ml @@ -1,17 +1,13 @@ -open Commons -open Pollinate.Node +open Pollinate +open Pollinate.PNode open Lwt.Infix - -module SUT = Pollinate.Node.Failure_detector +module SUT = Pollinate.PNode.Testing.Failure_detector let node_a = - Lwt_main.run - (Node.init ~preprocess:Commons.preprocess ~msg_handler:Commons.msg_handler - ("127.0.0.1", 3003)) + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 3003 }) + let node_b = - Lwt_main.run - (Node.init ~preprocess:Commons.preprocess ~msg_handler:Commons.msg_handler - ("127.0.0.1", 3004)) + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 3004 }) let peer_b = Client.peer_from !node_b @@ -19,7 +15,7 @@ let failure_detection () = let open Common.Peer in let open Client in let _ = add_peer !node_a peer_b in - let _ = Pollinate.Node.Client.peer_from !node_a in + let _ = Pollinate.PNode.Client.peer_from !node_a in let _ = SUT.update_peer_status node_a peer_b Suspicious in (* Need to wait for the timeout to be reached An other way to do, would be to change the `last_suspicious_status` of the peer *) let%lwt _ = Lwt_unix.sleep 9.1 in @@ -28,7 +24,7 @@ let failure_detection () = let failure_detection_nothing_on_alive () = let open Common.Peer in - let _ = add_neighbor (Pollinate.Node.Client.peer_from !node_a) peer_b in + let _ = add_neighbor (Pollinate.PNode.Client.peer_from !node_a) peer_b in let _ = SUT.update_peer_status node_a peer_b Alive in let%lwt _ = SUT.failure_detection node_a in Lwt.return (Base.Hashtbl.length !node_a.peers = 1) @@ -45,9 +41,9 @@ let () = [ ( "failure_detector.ml", [ - Alcotest_lwt.test_case "Remove Suspicious peer" `Quick + Alcotest_lwt.test_case "Remove Suspicious peer" `Slow test_suspicion_detection; - Alcotest_lwt.test_case "Do nothing on Alive peer" `Quick + Alcotest_lwt.test_case "Do nothing on Alive peer" `Slow test_failure_detection_nothing_on_alive; ] ); ] diff --git a/test/gossip_tests.ml b/test/gossip_tests.ml new file mode 100644 index 0000000..47701c0 --- /dev/null +++ b/test/gossip_tests.ml @@ -0,0 +1,171 @@ +open Lwt.Infix +open Commons +open Pollinate +open Pollinate.PNode + +module Gossip_tests = struct + let local_address port = Address.{ address = "127.0.0.1"; port } + + (* Initializes a group of nodes connected as shown here: https://tinyurl.com/tcy8dxu8 *) + let ( node_a, + node_b, + node_c, + node_d, + node_e, + node_f, + node_g, + node_h, + node_i, + node_j ) = + Lwt_main.run + begin + let ( addr_a, + addr_b, + addr_c, + addr_d, + addr_e, + addr_f, + addr_g, + addr_h, + addr_i, + addr_j ) = + ( local_address 4000, + local_address 4001, + local_address 4002, + local_address 4003, + local_address 4004, + local_address 4005, + local_address 4006, + local_address 4007, + local_address 4008, + local_address 4009 ) in + + let%lwt node_a = + PNode.init ~init_peers:[addr_b; addr_c; addr_e; addr_h] addr_a in + let%lwt node_b = + PNode.init ~init_peers:[addr_a; addr_d; addr_e] addr_b in + let%lwt node_c = + PNode.init ~init_peers:[addr_a; addr_f; addr_g] addr_c in + let%lwt node_d = PNode.init ~init_peers:[addr_b] addr_d in + let%lwt node_e = PNode.init ~init_peers:[addr_a; addr_b] addr_e in + let%lwt node_f = PNode.init ~init_peers:[addr_c] addr_f in + let%lwt node_g = PNode.init ~init_peers:[addr_c] addr_g in + let%lwt node_h = + PNode.init ~init_peers:[addr_a; addr_i; addr_j] addr_h in + let%lwt node_i = PNode.init ~init_peers:[addr_h] addr_i in + let%lwt node_j = PNode.init ~init_peers:[addr_h] addr_j in + Lwt.return + ( node_a, + node_b, + node_c, + node_d, + node_e, + node_f, + node_g, + node_h, + node_i, + node_j ) + end + + let nodes = + [ + node_a; + node_b; + node_c; + node_d; + node_e; + node_f; + node_g; + node_h; + node_i; + node_j; + ] + + (** Utility function for producing a list of ports from + the addresses of the given nodes. This provides an easy + way to identify nodes that are hosted on the same machine. *) + let node_ports nodes = + List.map + (fun n -> + let addr = Client.address_of !n in + addr.port) + nodes + + (** Starts the server for each node and constructs a Post message + whose author is the specified node, then disseminates it. Waits + 0.2 seconds for the message to disseminate fully across + the network. *) + let disseminate_from node = + (* Start the server for each node in a thread *) + let _ = + List.map + (PNode.run_server ~preprocessor:Commons.preprocessor + ~msg_handler:Commons.msg_handler) + nodes in + let payload = + Client.address_of !node + |> (fun Address.{ port; _ } -> port) + |> string_of_int + |> String.to_bytes in + let message = Client.create_post node (payload, None) in + Client.post node message; + + (* Function to generate a list of all the nodes who have witnessed + the post. *) + let seen () = + nodes |> List.filter (fun n -> PNode.seen n message) |> node_ports in + + (* Wait 0.2 seconds for the message to spread. *) + let%lwt () = Lwt_unix.sleep 0.2 in + + (* Compute the list of nodes who have seen the post. *) + let list_of_seen = seen () in + + (* Write the length of list_of_seen to a tmp log file *) + let%lwt () = + let%lwt oc = + Lwt_io.open_file + ~flags:[Unix.O_WRONLY; Unix.O_APPEND; Unix.O_CREAT] + ~mode:Lwt_io.Output "/tmp/log.txt" in + let%lwt () = + Lwt_io.write oc (Printf.sprintf "%d\n" (List.length list_of_seen)) in + Lwt_io.close oc in + (* End of logging code *) + Lwt.return list_of_seen +end + +(** Test for dissemination given a specific node. *) +let test_disseminate_from node _ () = + Gossip_tests.disseminate_from node + >|= Alcotest.(check (list int)) + (Printf.sprintf "All nodes have seen the message %d" !node.address.port) + Gossip_tests.(node_ports nodes) + +let () = + Lwt_main.run + @@ Alcotest_lwt.run "Gossip tests" + [ + ( "gossip dissemination", + [ + Alcotest_lwt.test_case "Dissemination from A" `Quick + (test_disseminate_from Gossip_tests.node_a); + Alcotest_lwt.test_case "Dissemination from B" `Quick + (test_disseminate_from Gossip_tests.node_b); + Alcotest_lwt.test_case "Dissemination from C" `Quick + (test_disseminate_from Gossip_tests.node_c); + Alcotest_lwt.test_case "Dissemination from D" `Quick + (test_disseminate_from Gossip_tests.node_d); + Alcotest_lwt.test_case "Dissemination from E" `Quick + (test_disseminate_from Gossip_tests.node_e); + Alcotest_lwt.test_case "Dissemination from F" `Quick + (test_disseminate_from Gossip_tests.node_f); + Alcotest_lwt.test_case "Dissemination from G" `Quick + (test_disseminate_from Gossip_tests.node_g); + Alcotest_lwt.test_case "Dissemination from H" `Quick + (test_disseminate_from Gossip_tests.node_h); + Alcotest_lwt.test_case "Dissemination from I" `Quick + (test_disseminate_from Gossip_tests.node_i); + Alcotest_lwt.test_case "Dissemination from J" `Quick + (test_disseminate_from Gossip_tests.node_j); + ] ); + ] diff --git a/test/networking_prop.ml b/test/networking_prop.ml new file mode 100644 index 0000000..b5e1115 --- /dev/null +++ b/test/networking_prop.ml @@ -0,0 +1,31 @@ +open QCheck2.Gen +open Pollinate.Peer +open Pollinate + +module SUT = Pollinate.PNode.Testing.Networking + +let node_a = + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 2002 }) + +let knuth_shuffle_size = + QCheck2.Test.make ~count:1000 + ~name:"Knuth_shuffle does not change the size of the list" + (QCheck2.Gen.list Generators.peer_gen) (fun peers -> + List.length (SUT.Testing.knuth_shuffle peers) == List.length peers) + +let pick_random_neighbors = + QCheck2.Test.make ~count:1000 + ~name: + "pick_random_neighbors on a peer with a single neighbor, returns this \ + neighbors" (pair Generators.peer_gen Generators.peer_gen) + (fun (peer, neighbor) -> + let _ = add_neighbor peer neighbor in + let random_neighbor = + List.hd @@ SUT.pick_random_neighbors peer.neighbors 1 in + random_neighbor == neighbor.address) + +let () = + let networking_prop = + List.map QCheck_alcotest.to_alcotest + [knuth_shuffle_size; pick_random_neighbors] in + Alcotest.run "Networking" [("networking.ml", networking_prop)] diff --git a/test/node_tests.ml b/test/node_tests.ml index c7e4574..f434c5e 100644 --- a/test/node_tests.ml +++ b/test/node_tests.ml @@ -1,22 +1,19 @@ open Lwt.Infix -open Pollinate.Node -open Pollinate.Util open Commons +open Pollinate +open Pollinate.PNode +open Pollinate.Util open Messages module Node_tests = struct (* Initializes two nodes and the related two peers *) let node_a = - Lwt_main.run - (Node.init ~preprocess:Commons.preprocess ~msg_handler:Commons.msg_handler - ("127.0.0.1", 3000)) + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 3000 }) let peer_a = Client.peer_from !node_a let node_b = - Lwt_main.run - (Node.init ~preprocess:Commons.preprocess ~msg_handler:Commons.msg_handler - ("127.0.0.1", 3001)) + Lwt_main.run (PNode.init Address.{ address = "127.0.0.1"; port = 3001 }) let peer_b = Client.peer_from !node_b @@ -24,49 +21,39 @@ module Node_tests = struct of the other, returning the first element in the response of each *) let trade_messages () = let open Messages in + let _ = + Lwt_list.map_p + (PNode.run_server ~preprocessor:Commons.preprocessor + ~msg_handler:Commons.msg_handler) + [node_a; node_b] in let get = Encoding.pack bin_writer_message (Request Get) in let%lwt { payload = res_from_b; _ } = - Client.request node_a get peer_b.address in + Client.request node_a (get, None) peer_b.address in let res_from_b = Encoding.unpack bin_read_response res_from_b in let%lwt { payload = res_from_a; _ } = - Client.request node_b get peer_a.address in + Client.request node_b (get, None) peer_a.address in let res_from_a = Encoding.unpack bin_read_response res_from_a in let res_from_b, res_from_a = match (res_from_b, res_from_a) with - | Success ok1, Success ok2 -> (ok1, ok2) + | Pong, Pong -> ("Ok", "Ok") | _ -> failwith "Incorrect response" in Lwt.return (res_from_b, res_from_a) - let test_insert () = - let open Messages in - let insert_req = - Encoding.pack bin_writer_message (Request (Insert "something")) in - - let%lwt { payload = res_a; _ } = - Client.request node_a insert_req peer_b.address in - let res_a = Encoding.unpack bin_read_response res_a in - - let get = Encoding.pack bin_writer_message (Request Get) in - let%lwt { payload = b_state; _ } = - Client.request node_a get peer_b.address in - let b_state = Encoding.unpack bin_read_response b_state in - - let res_a, b_state = - match (res_a, b_state) with - | Success ok1, Success ok2 -> (ok1, ok2) - | _ -> failwith "Incorrect response" in - - Lwt.return (res_a, b_state) - let ping_pong () = let open Messages in + let _ = + Lwt_list.map_p + (PNode.run_server ~preprocessor:Commons.preprocessor + ~msg_handler:Commons.msg_handler) + [node_a; node_b] in let ping = Encoding.pack bin_writer_message (Request Ping) in - let%lwt { payload = pong; _ } = Client.request node_a ping peer_b.address in + let%lwt { payload = pong; _ } = + Client.request node_a (ping, None) peer_b.address in let pong = Encoding.unpack bin_read_response pong in let pong = @@ -86,20 +73,13 @@ let test_trade_messages _ () = let test_ping_pong _ () = Node_tests.ping_pong () >|= Alcotest.(check string) "Ping pong" "Pong" -let test_insert_value _ () = - Node_tests.test_insert () - >|= Alcotest.(check (pair string string)) - "Test insert value" - ("Successfully added value to state", "Ok") - let () = Lwt_main.run @@ Alcotest_lwt.run "Client tests" [ - ( "communication", + ( "one-to-one communication", [ Alcotest_lwt.test_case "Trading Messages" `Quick test_trade_messages; Alcotest_lwt.test_case "Ping pong" `Quick test_ping_pong; - Alcotest_lwt.test_case "Insert value" `Quick test_insert_value; ] ); ]