diff --git a/docker-images/Dockerfile.oni-buildbase b/docker-images/Dockerfile.oni-buildbase index 1b3f87e4b..f9383b2d0 100644 --- a/docker-images/Dockerfile.oni-buildbase +++ b/docker-images/Dockerfile.oni-buildbase @@ -4,7 +4,7 @@ FROM golang:${GO_VERSION}-buster RUN apt-get update && apt-get install -y ca-certificates llvm clang mesa-opencl-icd ocl-icd-opencl-dev jq gcc git pkg-config bzr -ARG FILECOIN_FFI_COMMIT=ca281af0b6c00314382a75ae869e5cb22c83655b +ARG FILECOIN_FFI_COMMIT=5342c7c97d1a1df4650629d14f2823d52889edd9 ARG FFI_DIR=/extra/filecoin-ffi RUN mkdir -p ${FFI_DIR} \ diff --git a/docker-images/HISTORY.md b/docker-images/HISTORY.md new file mode 100644 index 000000000..7560da9e9 --- /dev/null +++ b/docker-images/HISTORY.md @@ -0,0 +1,12 @@ +# Docker image history + +## oni-buildbase + +* `v1` => initial image locking in FFI commit ca281af0b6c00314382a75ae869e5cb22c83655b. +* `v2` => no changes; released only for aligning both images to aesthetically please @nonsense :D +* `v3` => locking in FFI commit 5342c7c97d1a1df4650629d14f2823d52889edd9. + +## oni-runtime + +* `v1` => initial image with 2048 parameters. +* `v2` => adds auxiliary tools: `net-tools netcat traceroute iputils-ping wget vim curl telnet iproute2 dnsutils`. diff --git a/extra/filecoin-ffi b/extra/filecoin-ffi index ca281af0b..5342c7c97 160000 --- a/extra/filecoin-ffi +++ b/extra/filecoin-ffi @@ -1 +1 @@ -Subproject commit ca281af0b6c00314382a75ae869e5cb22c83655b +Subproject commit 5342c7c97d1a1df4650629d14f2823d52889edd9 diff --git a/lotus-soup/compositions/composition-drand-halt.toml b/lotus-soup/compositions/composition-drand-halt.toml new file mode 100644 index 000000000..658b7f12c --- /dev/null +++ b/lotus-soup/compositions/composition-drand-halt.toml @@ -0,0 +1,75 @@ +[metadata] + name = "lotus-soup" + author = "" + +[global] + plan = "lotus-soup" + case = "drand-halting" + total_instances = 6 + builder = "docker:go" + runner = "local:docker" + +[global.build] + selectors = ["testground"] + +[global.build_config] + enable_go_build_cache = true + +[global.run.test_params] + clients = "1" + miners = "1" + balance = "2000" + sectors = "10" + random_beacon_type = "local-drand" + genesis_timestamp_offset = "0" + +[[groups]] + id = "bootstrapper" + [groups.resources] + memory = "120Mi" + cpu = "10m" + [groups.instances] + count = 1 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "bootstrapper" + + +[[groups]] + id = "miners" + [groups.resources] + memory = "120Mi" + cpu = "10m" + [groups.instances] + count = 1 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "miner" + + +[[groups]] + id = "clients" + [groups.resources] + memory = "120Mi" + cpu = "10m" + [groups.instances] + count = 1 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "client" + + +[[groups]] + id = "drand" + [groups.instances] + count = 3 + percentage = 0.0 + [groups.run] + [groups.run.test_params] + role = "drand" + drand_period = "1s" + drand_log_level = "none" + suspend_events = "wait 20s -> halt -> wait 1m -> resume -> wait 2s -> halt -> wait 1m -> resume" diff --git a/lotus-soup/compositions/composition-local-drand.toml b/lotus-soup/compositions/composition-local-drand.toml index 50e969a49..6f517010e 100644 --- a/lotus-soup/compositions/composition-local-drand.toml +++ b/lotus-soup/compositions/composition-local-drand.toml @@ -9,6 +9,17 @@ builder = "docker:go" runner = "local:docker" +[global.build_config] + enable_go_build_cache = true + +[global.run.test_params] + clients = "1" + miners = "1" + balance = "2000" + sectors = "10" + random_beacon_type = "local-drand" + genesis_timestamp_offset = "0" + [[groups]] id = "bootstrapper" [groups.resources] @@ -20,11 +31,7 @@ [groups.run] [groups.run.test_params] role = "bootstrapper" - clients = "1" - miners = "1" - balance = "2000" - sectors = "10" - random_beacon_type = "local-drand" + [[groups]] id = "miners" @@ -37,12 +44,6 @@ [groups.run] [groups.run.test_params] role = "miner" - clients = "1" - miners = "1" - balance = "2000" - sectors = "10" - random_beacon_type = "local-drand" - [[groups]] id = "clients" @@ -55,12 +56,6 @@ [groups.run] [groups.run.test_params] role = "client" - clients = "1" - miners = "1" - balance = "2000" - sectors = "10" - random_beacon_type = "local-drand" - [[groups]] id = "drand" diff --git a/lotus-soup/compositions/composition.toml b/lotus-soup/compositions/composition.toml index cad741422..5a2b8b87f 100644 --- a/lotus-soup/compositions/composition.toml +++ b/lotus-soup/compositions/composition.toml @@ -12,6 +12,9 @@ [global.build_config] enable_go_build_cache = true +[global.build] + selectors = ["testground"] + [global.run.test_params] clients = "3" miners = "2" diff --git a/lotus-soup/go.mod b/lotus-soup/go.mod index 15a67024f..5fbdd9cd9 100644 --- a/lotus-soup/go.mod +++ b/lotus-soup/go.mod @@ -3,12 +3,13 @@ module github.com/filecoin-project/oni/lotus-soup go 1.14 require ( + github.com/davecgh/go-spew v1.1.1 github.com/drand/drand v0.9.2-0.20200616080806-a94e9c1636a4 github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef - github.com/filecoin-project/go-fil-markets v0.3.0 + github.com/filecoin-project/go-fil-markets v0.3.1 github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24 github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b - github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b + github.com/filecoin-project/lotus v0.4.1-0.20200701153027-70d83784046b github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121 github.com/gorilla/mux v1.7.4 github.com/influxdata/influxdb v1.8.0 // indirect @@ -16,7 +17,7 @@ require ( github.com/ipfs/go-datastore v0.4.4 github.com/ipfs/go-ipfs-files v0.0.8 github.com/ipfs/go-ipld-format v0.2.0 - github.com/ipfs/go-log/v2 v2.1.2-0.20200609205458-f8d20c392cb7 + github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 github.com/ipfs/go-merkledag v0.3.1 github.com/ipfs/go-unixfs v0.2.4 github.com/ipld/go-car v0.1.1-0.20200526133713-1c7508d55aae diff --git a/lotus-soup/go.sum b/lotus-soup/go.sum index ce096f015..5a58be51d 100644 --- a/lotus-soup/go.sum +++ b/lotus-soup/go.sum @@ -230,6 +230,8 @@ github.com/filecoin-project/go-bitfield v0.0.0-20200416002808-b3ee67ec9060/go.mo github.com/filecoin-project/go-bitfield v0.0.1/go.mod h1:Ry9/iUlWSyjPUzlAvdnfy4Gtvrq4kWmWDztCU1yEgJY= github.com/filecoin-project/go-bitfield v0.0.2-0.20200518150651-562fdb554b6e h1:gkG/7G+iKy4He+IiQNeQn+nndFznb/vCoOR8iRQsm60= github.com/filecoin-project/go-bitfield v0.0.2-0.20200518150651-562fdb554b6e/go.mod h1:Ry9/iUlWSyjPUzlAvdnfy4Gtvrq4kWmWDztCU1yEgJY= +github.com/filecoin-project/go-bitfield v0.0.2-0.20200629135455-587b27927d38 h1:B2gUde2DlfCb5YMYNVems2orobxC3KhrX3migym1IOQ= +github.com/filecoin-project/go-bitfield v0.0.2-0.20200629135455-587b27927d38/go.mod h1:Ry9/iUlWSyjPUzlAvdnfy4Gtvrq4kWmWDztCU1yEgJY= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2 h1:av5fw6wmm58FYMgJeoB/lK9XXrgdugYiTqkdxjTy9k8= github.com/filecoin-project/go-cbor-util v0.0.0-20191219014500-08c40a1e63a2/go.mod h1:pqTiPHobNkOVM5thSRsHYjyQfq7O5QSCMhvuu9JoDlg= github.com/filecoin-project/go-crypto v0.0.0-20191218222705-effae4ea9f03 h1:2pMXdBnCiXjfCYx/hLqFxccPoqsSveQFxVLvNxy9bus= @@ -238,16 +240,16 @@ github.com/filecoin-project/go-data-transfer v0.3.0 h1:BwBrrXu9Unh9JjjX4GAc5FfzU github.com/filecoin-project/go-data-transfer v0.3.0/go.mod h1:cONglGP4s/d+IUQw5mWZrQK+FQATQxr3AXzi4dRh0l4= github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5 h1:yvQJCW9mmi9zy+51xA01Ea2X7/dL7r8eKDPuGUjRmbo= github.com/filecoin-project/go-fil-commcid v0.0.0-20200208005934-2b8bd03caca5/go.mod h1:JbkIgFF/Z9BDlvrJO1FuKkaWsH673/UdFaiVS6uIHlA= -github.com/filecoin-project/go-fil-markets v0.3.0 h1:7iCGiuTSia4f4DmOn3s96NWUwMNSOI0ZHel/XgeApAQ= -github.com/filecoin-project/go-fil-markets v0.3.0/go.mod h1:UXsXi43AyUQ5ieb4yIaLgk4PVt7TAbl1UCccuNw+7ds= +github.com/filecoin-project/go-fil-markets v0.3.1 h1:YLH4ck4hQrKBpQ3fo0VcA2SXqiAosizxBJ/QHYgR9aE= +github.com/filecoin-project/go-fil-markets v0.3.1/go.mod h1:UY+/zwNXHN73HcrN6HxNDpv6KKM6ehqfCuE9vK9khF8= github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24 h1:Jc7vkplmZYVuaEcSXGHDwefvZIdoyyaoGDLqSr8Svms= github.com/filecoin-project/go-jsonrpc v0.1.1-0.20200602181149-522144ab4e24/go.mod h1:j6zV//WXIIY5kky873Q3iIKt/ViOE8rcijovmpxrXzM= github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6 h1:92PET+sx1Hb4W/8CgFwGuxaKbttwY+UNspYZTvXY0vs= github.com/filecoin-project/go-padreader v0.0.0-20200210211231-548257017ca6/go.mod h1:0HgYnrkeSU4lu1p+LEOeDpFsNBssa0OGGriWdA4hvaE= github.com/filecoin-project/go-paramfetch v0.0.1/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= github.com/filecoin-project/go-paramfetch v0.0.2-0.20200218225740-47c639bab663/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20200605171344-fcac609550ca h1:OGykrCr6mSn/ckk2IFbIlkc76nsgEs7tSLhZXQt7+z4= -github.com/filecoin-project/go-paramfetch v0.0.2-0.20200605171344-fcac609550ca/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261 h1:A256QonvzRaknIIAuWhe/M2dpV2otzs3NBhi5TWa/UA= +github.com/filecoin-project/go-paramfetch v0.0.2-0.20200701152213-3e0f0afdc261/go.mod h1:fZzmf4tftbwf9S37XRifoJlz7nCjRdIrMGLR07dKLCc= github.com/filecoin-project/go-statemachine v0.0.0-20200226041606-2074af6d51d9/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v0.0.0-20200612181802-4eb3d0c68eba h1:GEWb/6KQyNZt4jm8fgVcIFPH0ElAGXfHM59ZSiqPTvY= github.com/filecoin-project/go-statemachine v0.0.0-20200612181802-4eb3d0c68eba/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= @@ -255,21 +257,23 @@ github.com/filecoin-project/go-statestore v0.1.0 h1:t56reH59843TwXHkMcwyuayStBIi github.com/filecoin-project/go-statestore v0.1.0/go.mod h1:LFc9hD+fRxPqiHiaqUEZOinUJB4WARkRfNl10O7kTnI= github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b h1:fkRZSPrYpk42PV3/lIXiL0LHetxde7vyYYvSsttQtfg= github.com/filecoin-project/go-storedcounter v0.0.0-20200421200003-1c99c62e8a5b/go.mod h1:Q0GQOBtKf1oE10eSXSlhN45kDBdGvEcVOqMiffqX+N8= -github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b h1:Oq1ABSZVYNFtvWqnj3bHINcw34T0if/0/zlsRq8rGgc= -github.com/filecoin-project/lotus v0.4.1-0.20200623211458-e8642442267b/go.mod h1:uxvEKQiyuXisy/7MB6pFwb4WxxcA3UY3hyJnhL+k7M4= +github.com/filecoin-project/lotus v0.4.1-0.20200701153027-70d83784046b h1:5B4Sabzrk8lH+AD/yTUrL9yJT4AemAEtRJMHrciA7bk= +github.com/filecoin-project/lotus v0.4.1-0.20200701153027-70d83784046b/go.mod h1:hqGdmSDzUWfhss1qBHot1Yi35AHFFJ6NVuFVZJLKW1M= github.com/filecoin-project/sector-storage v0.0.0-20200615154852-728a47ab99d6/go.mod h1:M59QnAeA/oV+Z8oHFLoNpGMv0LZ8Rll+vHVXX7GirPM= -github.com/filecoin-project/sector-storage v0.0.0-20200618073200-d9de9b7cb4b4 h1:lQC8Fbyn31/H4QxYAYwVV3PYZ9vS61EmjktZc5CaiYs= -github.com/filecoin-project/sector-storage v0.0.0-20200618073200-d9de9b7cb4b4/go.mod h1:M59QnAeA/oV+Z8oHFLoNpGMv0LZ8Rll+vHVXX7GirPM= +github.com/filecoin-project/sector-storage v0.0.0-20200625154333-98ef8e4ef246/go.mod h1:8f0hWDzzIi1hKs4IVKH9RnDsO4LEHVz8BNat0okDOuY= +github.com/filecoin-project/sector-storage v0.0.0-20200630180318-4c1968f62a8f h1:EHKqNJNIcYggqfrd5nu7SV1KR93ReZygfdSV0w/jefQ= +github.com/filecoin-project/sector-storage v0.0.0-20200630180318-4c1968f62a8f/go.mod h1:r12d7tsmJKz8QDGoCvl65Ay2al6mOgDqxAGUxbyrgMs= github.com/filecoin-project/specs-actors v0.0.0-20200210130641-2d1fbd8672cf/go.mod h1:xtDZUB6pe4Pksa/bAJbJ693OilaC5Wbot9jMhLm3cZA= github.com/filecoin-project/specs-actors v0.3.0/go.mod h1:nQYnFbQ7Y0bHZyq6HDEuVlCPR+U3z5Q3wMOQ+2aiV+Y= github.com/filecoin-project/specs-actors v0.6.0/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY= github.com/filecoin-project/specs-actors v0.6.1/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY= github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121 h1:oRA+b4iN4H86xXDXbU3TOyvmBZp7//c5VqTc0oJ6nLg= github.com/filecoin-project/specs-actors v0.6.2-0.20200617175406-de392ca14121/go.mod h1:dRdy3cURykh2R8O/DKqy8olScl70rmIS7GrB4hB1IDY= -github.com/filecoin-project/specs-storage v0.1.0 h1:PkDgTOT5W5Ao7752onjDl4QSv+sgOVdJbvFjOnD5w94= github.com/filecoin-project/specs-storage v0.1.0/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k= -github.com/filecoin-project/storage-fsm v0.0.0-20200617183754-4380106d3e94 h1:zPKiZPMgkFF0Lq13hsk8lcWlxeVAs6vvJaa3uHn9v70= -github.com/filecoin-project/storage-fsm v0.0.0-20200617183754-4380106d3e94/go.mod h1:q1YCutTSMq/yGYvDPHReT37bPfDLHltnwJutzR9kOY0= +github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea h1:iixjULRQFPn7Q9KlIqfwLJnlAXO10bbkI+xy5GKGdLY= +github.com/filecoin-project/specs-storage v0.1.1-0.20200622113353-88a9704877ea/go.mod h1:Pr5ntAaxsh+sLG/LYiL4tKzvA83Vk5vLODYhfNwOg7k= +github.com/filecoin-project/storage-fsm v0.0.0-20200625160832-379a4655b044 h1:i4oMhv1kx/MAUxRN4EM5tag5fI1uagrwQwINgKrzUt4= +github.com/filecoin-project/storage-fsm v0.0.0-20200625160832-379a4655b044/go.mod h1:JD7fmV1BYADDcy4EYQnqFH/rUzXsh0Je0jXarCjZqSk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= @@ -478,8 +482,9 @@ github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJ github.com/ipfs/go-blockservice v0.0.3/go.mod h1:/NNihwTi6V2Yr6g8wBI+BSwPuURpBRMtYNGrlxZ8KuI= github.com/ipfs/go-blockservice v0.0.7/go.mod h1:EOfb9k/Y878ZTRY/CH0x5+ATtaipfbRhbvNSdgc/7So= github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.1.3 h1:9XgsPMwwWJSC9uVr2pMDsW2qFTBSkxpGMhmna8mIjPM= github.com/ipfs/go-blockservice v0.1.3/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= +github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834 h1:hFJoI1D2a3MqiNkSb4nKwrdkhCngUxUTFNwVwovZX2s= +github.com/ipfs/go-blockservice v0.1.4-0.20200624145336-a978cec6e834/go.mod h1:OTZhFpkgY48kNzbgyvcexW9cHrpjBYIjSR0KoDOFOLU= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= @@ -598,8 +603,8 @@ github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.2-0.20200609205458-f8d20c392cb7 h1:LtL/rvdfbKSthZGmAAD9o4KKg6HA6Qn8gXCCdgnj7lw= -github.com/ipfs/go-log/v2 v2.1.2-0.20200609205458-f8d20c392cb7/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= +github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4 h1:3bijxqzQ1O9yg7gd7Aqk80oaEvsJ+uXw0zSvi2qR3Jw= +github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-merkledag v0.0.3/go.mod h1:Oc5kIXLHokkE1hWGMBHw+oxehkAaTOqtEb7Zbh6BhLA= github.com/ipfs/go-merkledag v0.0.6/go.mod h1:QYPdnlvkOg7GnQRofu9XZimC5ZW5Wi3bKys/4GQQfto= github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= diff --git a/lotus-soup/main.go b/lotus-soup/main.go index 139816d77..f2e91bd7e 100644 --- a/lotus-soup/main.go +++ b/lotus-soup/main.go @@ -9,6 +9,7 @@ import ( var cases = map[string]interface{}{ "deals-e2e": testkit.WrapTestEnvironment(dealsE2E), "deals-stress-test": testkit.WrapTestEnvironment(dealStressTest), + "drand-halting": testkit.WrapTestEnvironment(dealsE2E), } func main() { diff --git a/lotus-soup/manifest.toml b/lotus-soup/manifest.toml index 957831b27..643c75737 100644 --- a/lotus-soup/manifest.toml +++ b/lotus-soup/manifest.toml @@ -7,8 +7,8 @@ runner = "local:docker" [builders."docker:go"] enabled = true -build_base_image = "iptestground/oni-buildbase:v1" -runtime_image = "iptestground/oni-runtime:v1" +build_base_image = "iptestground/oni-buildbase:v3" +runtime_image = "iptestground/oni-runtime:v2" [runners."local:docker"] enabled = true @@ -35,14 +35,41 @@ instances = { min = 1, max = 100, default = 5 } # in the same composition group. There must be at least threshold drand nodes. # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand" # for the lotus node groups. - drand_period = { type = "duration", default="10s" } - drand_threshold = { type = "int", default = 2 } - drand_gossip_relay = { type = "bool", default = true } + drand_period = { type = "duration", default="10s" } + drand_threshold = { type = "int", default = 2 } + drand_gossip_relay = { type = "bool", default = true } + drand_log_level = { type = "string", default="info" } # Params relevant to pubsub tracing enable_pubsub_tracer = { type = "bool", default = false } - # Mining Mode: synchronized -vs- natural time +[[testcases]] +name = "drand-halting" +instances = { min = 1, max = 100, default = 5 } + + [testcases.params] + clients = { type = "int", default = 1 } + miners = { type = "int", default = 1 } + balance = { type = "int", default = 1 } + sectors = { type = "int", default = 1 } + role = { type = "string" } + genesis_timestamp_offset = { type = "int", default = 0 } + + + random_beacon_type = { type = "enum", default = "local-drand", options = ["mock", "local-drand", "external-drand"] } + + # Params relevant to drand nodes. drand nodes should have role="drand", and must all be + # in the same composition group. There must be at least threshold drand nodes. + # To get lotus nodes to actually use the drand nodes, you must set random_beacon_type="local-drand" + # for the lotus node groups. + drand_period = { type = "duration", default="10s" } + drand_threshold = { type = "int", default = 2 } + drand_gossip_relay = { type = "bool", default = true } + drand_log_level = { type = "string", default="info" } + suspend_events = { type = "string", default="", desc = "a sequence of halt/resume/wait events separated by '->'" } + + # Params relevant to pubsub tracing + enable_pubsub_tracer = { type = "bool", default = false } # Mining Mode: synchronized -vs- natural time mining_mode = { type = "enum", default = "synchronized", options = ["synchronized", "natural"] } [[testcases]] diff --git a/lotus-soup/statemachine/statemachine.go b/lotus-soup/statemachine/statemachine.go new file mode 100644 index 000000000..17de614db --- /dev/null +++ b/lotus-soup/statemachine/statemachine.go @@ -0,0 +1,108 @@ +package statemachine + +import ( + "errors" + "sync" +) + +// This code has been shamelessly lifted from this blog post: +// https://venilnoronha.io/a-simple-state-machine-framework-in-go +// Many thanks to the author, Venil Norohnha + +// ErrEventRejected is the error returned when the state machine cannot process +// an event in the state that it is in. +var ErrEventRejected = errors.New("event rejected") + +const ( + // Default represents the default state of the system. + Default StateType = "" + + // NoOp represents a no-op event. + NoOp EventType = "NoOp" +) + +// StateType represents an extensible state type in the state machine. +type StateType string + +// EventType represents an extensible event type in the state machine. +type EventType string + +// EventContext represents the context to be passed to the action implementation. +type EventContext interface{} + +// Action represents the action to be executed in a given state. +type Action interface { + Execute(eventCtx EventContext) EventType +} + +// Events represents a mapping of events and states. +type Events map[EventType]StateType + +// State binds a state with an action and a set of events it can handle. +type State struct { + Action Action + Events Events +} + +// States represents a mapping of states and their implementations. +type States map[StateType]State + +// StateMachine represents the state machine. +type StateMachine struct { + // Previous represents the previous state. + Previous StateType + + // Current represents the current state. + Current StateType + + // States holds the configuration of states and events handled by the state machine. + States States + + // mutex ensures that only 1 event is processed by the state machine at any given time. + mutex sync.Mutex +} + +// getNextState returns the next state for the event given the machine's current +// state, or an error if the event can't be handled in the given state. +func (s *StateMachine) getNextState(event EventType) (StateType, error) { + if state, ok := s.States[s.Current]; ok { + if state.Events != nil { + if next, ok := state.Events[event]; ok { + return next, nil + } + } + } + return Default, ErrEventRejected +} + +// SendEvent sends an event to the state machine. +func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error { + s.mutex.Lock() + defer s.mutex.Unlock() + + for { + // Determine the next state for the event given the machine's current state. + nextState, err := s.getNextState(event) + if err != nil { + return ErrEventRejected + } + + // Identify the state definition for the next state. + state, ok := s.States[nextState] + if !ok || state.Action == nil { + // configuration error + } + + // Transition over to the next state. + s.Previous = s.Current + s.Current = nextState + + // Execute the next state's action and loop over again if the event returned + // is not a no-op. + nextEvent := state.Action.Execute(eventCtx) + if nextEvent == NoOp { + return nil + } + event = nextEvent + } +} diff --git a/lotus-soup/statemachine/suspend.go b/lotus-soup/statemachine/suspend.go new file mode 100644 index 000000000..11bade7c1 --- /dev/null +++ b/lotus-soup/statemachine/suspend.go @@ -0,0 +1,128 @@ +package statemachine + +import ( + "fmt" + "strings" + "time" +) + +const ( + Running StateType = "running" + Suspended StateType = "suspended" + + Halt EventType = "halt" + Resume EventType = "resume" +) + +type Suspendable interface { + Halt() + Resume() +} + +type HaltAction struct{} + +func (a *HaltAction) Execute(ctx EventContext) EventType { + s, ok := ctx.(*Suspender) + if !ok { + fmt.Println("unable to halt, event context is not Suspendable") + return NoOp + } + s.target.Halt() + return NoOp +} + +type ResumeAction struct{} + +func (a *ResumeAction) Execute(ctx EventContext) EventType { + s, ok := ctx.(*Suspender) + if !ok { + fmt.Println("unable to resume, event context is not Suspendable") + return NoOp + } + s.target.Resume() + return NoOp +} + +type Suspender struct { + StateMachine + target Suspendable + log LogFn +} + +type LogFn func(fmt string, args ...interface{}) + +func NewSuspender(target Suspendable, log LogFn) *Suspender { + return &Suspender{ + target: target, + log: log, + StateMachine: StateMachine{ + Current: Running, + States: States{ + Running: State{ + Action: &ResumeAction{}, + Events: Events{ + Halt: Suspended, + }, + }, + + Suspended: State{ + Action: &HaltAction{}, + Events: Events{ + Resume: Running, + }, + }, + }, + }, + } +} + +func (s *Suspender) RunEvents(eventSpec string) { + s.log("running event spec: %s", eventSpec) + for _, et := range parseEventSpec(eventSpec, s.log) { + if et.delay != 0 { + //s.log("waiting %s", et.delay.String()) + time.Sleep(et.delay) + continue + } + if et.event == "" { + s.log("ignoring empty event") + continue + } + s.log("sending event %s", et.event) + err := s.SendEvent(et.event, s) + if err != nil { + s.log("error sending event %s: %s", et.event, err) + } + } +} + +type eventTiming struct { + delay time.Duration + event EventType +} + +func parseEventSpec(spec string, log LogFn) []eventTiming { + fields := strings.Split(spec, "->") + out := make([]eventTiming, 0, len(fields)) + for _, f := range fields { + f = strings.TrimSpace(f) + words := strings.Split(f, " ") + + // TODO: try to implement a "waiting" state instead of special casing like this + if words[0] == "wait" { + if len(words) != 2 { + log("expected 'wait' to be followed by duration, e.g. 'wait 30s'. ignoring.") + continue + } + d, err := time.ParseDuration(words[1]) + if err != nil { + log("bad argument for 'wait': %s", err) + continue + } + out = append(out, eventTiming{delay: d}) + } else { + out = append(out, eventTiming{event: EventType(words[0])}) + } + } + return out +} diff --git a/lotus-soup/testkit/role_drand.go b/lotus-soup/testkit/role_drand.go index ec285b9f9..0376b4132 100644 --- a/lotus-soup/testkit/role_drand.go +++ b/lotus-soup/testkit/role_drand.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/hex" - "encoding/json" "fmt" "io/ioutil" "net" @@ -13,70 +12,198 @@ import ( "time" "github.com/drand/drand/chain" + "github.com/drand/drand/client" hclient "github.com/drand/drand/client/http" - "github.com/drand/drand/demo/node" + "github.com/drand/drand/core" + "github.com/drand/drand/key" "github.com/drand/drand/log" "github.com/drand/drand/lp2p" + dnet "github.com/drand/drand/net" + "github.com/drand/drand/protobuf/drand" + dtest "github.com/drand/drand/test" "github.com/filecoin-project/lotus/node/modules/dtypes" "github.com/libp2p/go-libp2p-core/peer" ma "github.com/multiformats/go-multiaddr" "github.com/testground/sdk-go/sync" + + "github.com/filecoin-project/oni/lotus-soup/statemachine" ) -var PrepareDrandTimeout = time.Minute +var ( + PrepareDrandTimeout = time.Minute + secretDKG = "dkgsecret" +) type DrandInstance struct { - t *TestEnvironment - - Node node.Node - GossipRelay *lp2p.GossipRelayNode + daemon *core.Drand + httpClient client.Client + ctrlClient *dnet.ControlClient + gossipRelay *lp2p.GossipRelayNode + t *TestEnvironment stateDir string + priv *key.Pair + pubAddr string + privAddr string + ctrlAddr string } -func (d *DrandInstance) Cleanup() error { - return os.RemoveAll(d.stateDir) -} - -func (d *DrandInstance) RunDefault() error { - d.t.RecordMessage("running drand node") - defer d.Cleanup() - - // TODO add ability to halt / recover on demand - d.t.WaitUntilAllDone() +func (dr *DrandInstance) Start() error { + opts := []core.ConfigOption{ + core.WithLogLevel(getLogLevel(dr.t)), + core.WithConfigFolder(dr.stateDir), + core.WithPublicListenAddress(dr.pubAddr), + core.WithPrivateListenAddress(dr.privAddr), + core.WithControlPort(dr.ctrlAddr), + core.WithInsecure(), + } + conf := core.NewConfig(opts...) + fs := key.NewFileStore(conf.ConfigFolder()) + fs.SaveKeyPair(dr.priv) + key.Save(path.Join(dr.stateDir, "public.toml"), dr.priv.Public, false) + if dr.daemon == nil { + drand, err := core.NewDrand(fs, conf) + if err != nil { + return err + } + dr.daemon = drand + } else { + drand, err := core.LoadDrand(fs, conf) + if err != nil { + return err + } + drand.StartBeacon(true) + dr.daemon = drand + } return nil } -// PrepareDrandInstance starts a drand instance and runs a DKG with the other -// members of the composition group. -// -// Once the chain is running, the leader publishes the chain info needed by -// lotus nodes on DrandConfigTopic. +func (dr *DrandInstance) Ping() bool { + cl := dr.ctrl() + if err := cl.Ping(); err != nil { + return false + } + return true +} + +func (dr *DrandInstance) Close() error { + dr.gossipRelay.Shutdown() + dr.daemon.Stop(context.Background()) + return os.RemoveAll(dr.stateDir) +} + +func (dr *DrandInstance) ctrl() *dnet.ControlClient { + if dr.ctrlClient != nil { + return dr.ctrlClient + } + cl, err := dnet.NewControlClient(dr.ctrlAddr) + if err != nil { + dr.t.RecordMessage("drand can't instantiate control client: %w", err) + return nil + } + dr.ctrlClient = cl + return cl +} + +func (dr *DrandInstance) RunDKG(nodes, thr int, timeout string, leader bool, leaderAddr string, beaconOffset int) *key.Group { + cl := dr.ctrl() + p := dr.t.DurationParam("drand_period") + t, _ := time.ParseDuration(timeout) + var grp *drand.GroupPacket + var err error + if leader { + grp, err = cl.InitDKGLeader(nodes, thr, p, t, nil, secretDKG, beaconOffset) + } else { + leader := dnet.CreatePeer(leaderAddr, false) + grp, err = cl.InitDKG(leader, nil, secretDKG) + } + if err != nil { + dr.t.RecordMessage("drand dkg run failed: %w", err) + return nil + } + kg, _ := key.GroupFromProto(grp) + return kg +} + +func (dr *DrandInstance) Halt() { + dr.t.RecordMessage("drand node #%d halting", dr.t.GroupSeq) + dr.daemon.StopBeacon() +} + +func (dr *DrandInstance) Resume() { + dr.t.RecordMessage("drand node #%d resuming", dr.t.GroupSeq) + dr.daemon.StartBeacon(true) + // block until we can fetch the round corresponding to the current time + startTime := time.Now() + round := dr.httpClient.RoundAt(startTime) + timeout := 30 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + done := make(chan struct{}, 1) + go func() { + for { + res, err := dr.httpClient.Get(ctx, round) + if err == nil { + dr.t.RecordMessage("drand chain caught up to round %d", res.Round()) + done <- struct{}{} + return + } + } + }() + + select { + case <-ctx.Done(): + dr.t.RecordMessage("drand chain failed to catch up after %s", timeout.String()) + case <-done: + dr.t.RecordMessage("drand chain resumed after %s catchup time", time.Since(startTime)) + } +} + +func (dr *DrandInstance) RunDefault() error { + dr.t.RecordMessage("running drand node") + + if dr.t.IsParamSet("suspend_events") { + suspender := statemachine.NewSuspender(dr, dr.t.RecordMessage) + suspender.RunEvents(dr.t.StringParam("suspend_events")) + } + + dr.t.WaitUntilAllDone() + return nil +} + +// prepareDrandNode starts a drand instance and runs a DKG with the other members of the composition group. +// Once the chain is running, the leader publishes the chain info needed by lotus nodes on +// drandConfigTopic func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { - var ( - startTime = time.Now() - seq = t.GroupSeq - isLeader = seq == 1 - nNodes = t.TestGroupInstanceCount - - myAddr = t.NetClient.MustGetDataNetworkIP() - period = t.DurationParam("drand_period") - threshold = t.IntParam("drand_threshold") - runGossipRelay = t.BooleanParam("drand_gossip_relay") - - beaconOffset = 3 - ) - ctx, cancel := context.WithTimeout(context.Background(), PrepareDrandTimeout) defer cancel() - stateDir, err := ioutil.TempDir("", fmt.Sprintf("drand-%d", t.GroupSeq)) + startTime := time.Now() + + seq := t.GroupSeq + isLeader := seq == 1 + nNodes := t.TestGroupInstanceCount + + myAddr := t.NetClient.MustGetDataNetworkIP() + threshold := t.IntParam("drand_threshold") + runGossipRelay := t.BooleanParam("drand_gossip_relay") + + beaconOffset := 3 + + stateDir, err := ioutil.TempDir("/tmp", fmt.Sprintf("drand-%d", t.GroupSeq)) if err != nil { return nil, err } - // TODO(maybe): use TLS? - n := node.NewLocalNode(int(seq), period.String(), stateDir, false, myAddr.String()) + dr := DrandInstance{ + t: t, + stateDir: stateDir, + pubAddr: dtest.FreeBind(myAddr.String()), + privAddr: dtest.FreeBind(myAddr.String()), + ctrlAddr: dtest.FreeBind("localhost"), + } + dr.priv = key.NewKeyPair(dr.privAddr) // share the node addresses with other nodes // TODO: if we implement TLS, this is where we'd share public TLS keys @@ -85,17 +212,15 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { PublicAddr string IsLeader bool } - addrTopic := sync.NewTopic("drand-addrs", &NodeAddr{}) var publicAddrs []string var leaderAddr string ch := make(chan *NodeAddr) _, sub := t.SyncClient.MustPublishSubscribe(ctx, addrTopic, &NodeAddr{ - PrivateAddr: n.PrivateAddr(), - PublicAddr: n.PublicAddr(), + PrivateAddr: dr.privAddr, + PublicAddr: dr.pubAddr, IsLeader: isLeader, }, ch) - for i := 0; i < nNodes; i++ { select { case msg := <-ch: @@ -107,21 +232,20 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { return nil, fmt.Errorf("unable to read drand addrs from sync service: %w", err) } } - if leaderAddr == "" { return nil, fmt.Errorf("got %d drand addrs, but no leader", len(publicAddrs)) } t.SyncClient.MustSignalAndWait(ctx, "drand-start", nNodes) t.RecordMessage("Starting drand sharing ceremony") - if err := n.Start(stateDir); err != nil { + if err := dr.Start(); err != nil { return nil, err } alive := false waitSecs := 10 for i := 0; i < waitSecs; i++ { - if !n.Ping() { + if !dr.Ping() { time.Sleep(time.Second) continue } @@ -138,7 +262,7 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { if !isLeader { time.Sleep(time.Second) } - grp := n.RunDKG(nNodes, threshold, period.String(), isLeader, leaderAddr, beaconOffset) + grp := dr.RunDKG(nNodes, threshold, "10s", isLeader, leaderAddr, beaconOffset) if grp == nil { return nil, fmt.Errorf("drand dkg failed") } @@ -152,19 +276,18 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { t.RecordMessage("drand beacon chain started, fetching initial round via http") // verify that we can get a round of randomness from the chain using an http client info := chain.NewChainInfo(grp) - myPublicAddr := fmt.Sprintf("http://%s", n.PublicAddr()) - client, err := hclient.NewWithInfo(myPublicAddr, info, nil) + myPublicAddr := fmt.Sprintf("http://%s", dr.pubAddr) + dr.httpClient, err = hclient.NewWithInfo(myPublicAddr, info, nil) if err != nil { return nil, fmt.Errorf("unable to create drand http client: %w", err) } - _, err = client.Get(ctx, 1) + _, err = dr.httpClient.Get(ctx, 1) if err != nil { return nil, fmt.Errorf("unable to get initial drand round: %w", err) } // start gossip relay (unless disabled via testplan parameter) - var gossipRelay *lp2p.GossipRelayNode var relayAddrs []peer.AddrInfo if runGossipRelay { @@ -176,17 +299,17 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { DataDir: gossipDir, IdentityPath: path.Join(gossipDir, "identity.key"), Insecure: true, - Client: client, + Client: dr.httpClient, } t.RecordMessage("starting drand gossip relay") - gossipRelay, err = lp2p.NewGossipRelayNode(log.DefaultLogger, &relayCfg) + dr.gossipRelay, err = lp2p.NewGossipRelayNode(log.NewLogger(getLogLevel(t)), &relayCfg) if err != nil { return nil, fmt.Errorf("failed to construct drand gossip relay: %w", err) } t.RecordMessage("sharing gossip relay addrs") // share the gossip relay addrs so we can publish them in DrandRuntimeInfo - relayInfo, err := relayAddrInfo(gossipRelay.Multiaddrs(), myAddr) + relayInfo, err := relayAddrInfo(dr.gossipRelay.Multiaddrs(), myAddr) if err != nil { return nil, err } @@ -217,17 +340,13 @@ func PrepareDrandInstance(t *TestEnvironment) (*DrandInstance, error) { }, GossipBootstrap: relayAddrs, } - dump, _ := json.Marshal(cfg) - t.RecordMessage("publishing drand config on sync topic: %s", string(dump)) + t.DebugSpew("publishing drand config on sync topic: %v", cfg) t.SyncClient.MustPublish(ctx, DrandConfigTopic, &cfg) } - return &DrandInstance{ - t: t, - Node: n, - GossipRelay: gossipRelay, - stateDir: stateDir, - }, nil + // signal ready state + t.SyncClient.MustSignalAndWait(ctx, StateReady, t.TestInstanceCount) + return &dr, nil } // waitForDrandConfig should be called by filecoin instances before constructing the lotus Node @@ -252,3 +371,14 @@ func relayAddrInfo(addrs []ma.Multiaddr, dataIP net.IP) (*peer.AddrInfo, error) } return nil, fmt.Errorf("no addr found with data ip %s in addrs: %v", dataIP, addrs) } + +func getLogLevel(t *TestEnvironment) int { + switch t.StringParam("drand_log_level") { + case "info": + return log.LogInfo + case "debug": + return log.LogDebug + default: + return log.LogNone + } +} diff --git a/lotus-soup/testkit/role_miner.go b/lotus-soup/testkit/role_miner.go index 200d0ef39..a3980e2d3 100644 --- a/lotus-soup/testkit/role_miner.go +++ b/lotus-soup/testkit/role_miner.go @@ -298,8 +298,8 @@ func (m *LotusMiner) RunDefault() error { ) t.RecordMessage("running miner") - t.RecordMessage("block delay: %v", build.BlockDelay) - t.D().Gauge("miner.block-delay").Update(build.BlockDelay) + t.RecordMessage("block delay: %v", build.BlockDelaySecs) + t.D().Gauge("miner.block-delay").Update(float64(build.BlockDelaySecs)) ctx := context.Background() myActorAddr, err := m.MinerApi.ActorAddress(ctx) diff --git a/lotus-soup/testkit/testenv.go b/lotus-soup/testkit/testenv.go index d770d3dd3..c9c2f8a53 100644 --- a/lotus-soup/testkit/testenv.go +++ b/lotus-soup/testkit/testenv.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/davecgh/go-spew/spew" + "github.com/testground/sdk-go/run" "github.com/testground/sdk-go/runtime" ) @@ -30,6 +32,10 @@ func (t *TestEnvironment) DurationParam(name string) time.Duration { return d } +func (t *TestEnvironment) DebugSpew(format string, args... interface{}) { + t.RecordMessage(spew.Sprintf(format, args...)) +} + // WaitUntilAllDone waits until all instances in the test case are done. func (t *TestEnvironment) WaitUntilAllDone() { ctx := context.Background()