diff --git a/examples/gno.land/p/demo/watchdog/gno.mod b/examples/gno.land/p/demo/watchdog/gno.mod new file mode 100644 index 00000000000..29005441401 --- /dev/null +++ b/examples/gno.land/p/demo/watchdog/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/watchdog + +require gno.land/p/demo/uassert v0.0.0-latest diff --git a/examples/gno.land/p/demo/watchdog/watchdog.gno b/examples/gno.land/p/demo/watchdog/watchdog.gno new file mode 100644 index 00000000000..6b4591b4938 --- /dev/null +++ b/examples/gno.land/p/demo/watchdog/watchdog.gno @@ -0,0 +1,39 @@ +package watchdog + +import "time" + +type Watchdog struct { + Duration time.Duration + lastUpdate time.Time + lastDown time.Time +} + +func (w *Watchdog) Alive() { + now := time.Now() + if !w.IsAlive() { + w.lastDown = now + } + w.lastUpdate = now +} + +func (w Watchdog) Status() string { + if w.IsAlive() { + return "OK" + } + return "KO" +} + +func (w Watchdog) IsAlive() bool { + return time.Since(w.lastUpdate) < w.Duration +} + +func (w Watchdog) UpSince() time.Time { + return w.lastDown +} + +func (w Watchdog) DownSince() time.Time { + if !w.IsAlive() { + return w.lastUpdate + } + return time.Time{} +} diff --git a/examples/gno.land/p/demo/watchdog/watchdog_test.gno b/examples/gno.land/p/demo/watchdog/watchdog_test.gno new file mode 100644 index 00000000000..eb7121fc4c3 --- /dev/null +++ b/examples/gno.land/p/demo/watchdog/watchdog_test.gno @@ -0,0 +1,16 @@ +package watchdog + +import ( + "testing" + "time" + + "gno.land/p/demo/uassert" +) + +func TestPackage(t *testing.T) { + w := Watchdog{Duration: 5 * time.Minute} + uassert.False(t, w.IsAlive()) + w.Alive() + uassert.True(t, w.IsAlive()) + // XXX: add more tests when we'll be able to "skip time". +} diff --git a/examples/gno.land/r/gnoland/monit/gno.mod b/examples/gno.land/r/gnoland/monit/gno.mod new file mode 100644 index 00000000000..e67fdaa7d71 --- /dev/null +++ b/examples/gno.land/r/gnoland/monit/gno.mod @@ -0,0 +1,8 @@ +module gno.land/r/gnoland/monit + +require ( + gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/watchdog v0.0.0-latest +) diff --git a/examples/gno.land/r/gnoland/monit/monit.gno b/examples/gno.land/r/gnoland/monit/monit.gno new file mode 100644 index 00000000000..8747ea582b3 --- /dev/null +++ b/examples/gno.land/r/gnoland/monit/monit.gno @@ -0,0 +1,59 @@ +// Package monit links a monitoring system with the chain in both directions. +// +// The agent will periodically call Incr() and verify that the value is always +// higher than the previously known one. The contract will store the last update +// time and use it to detect whether or not the monitoring agent is functioning +// correctly. +package monit + +import ( + "std" + "time" + + "gno.land/p/demo/ownable" + "gno.land/p/demo/ufmt" + "gno.land/p/demo/watchdog" +) + +var ( + counter int + lastUpdate time.Time + lastCaller std.Address + wd = watchdog.Watchdog{Duration: 5 * time.Minute} + owner = ownable.New() // TODO: replace with -> ownable.NewWithAddress... + watchdogDuration = 5 * time.Minute +) + +// Incr increments the counter and informs the watchdog that we're alive. +// This function can be called by anyone. +func Incr() int { + counter++ + lastUpdate = time.Now() + lastCaller = std.PrevRealm().Addr() + wd.Alive() + return counter +} + +// Reset resets the realm state. +// This function can only be called by the admin. +func Reset() { + if owner.CallerIsOwner() != nil { // TODO: replace with owner.AssertCallerIsOwner + panic("unauthorized") + } + counter = 0 + lastCaller = std.PrevRealm().Addr() + lastUpdate = time.Now() + wd = watchdog.Watchdog{Duration: 5 * time.Minute} +} + +func Render(_ string) string { + status := wd.Status() + return ufmt.Sprintf( + "counter=%d\nlast update=%s\nlast caller=%s\nstatus=%s", + counter, lastUpdate, lastCaller, status, + ) +} + +// TransferOwnership transfers ownership to a new owner. This is a proxy to +// ownable.Ownable.TransferOwnership. +func TransferOwnership(newOwner std.Address) { owner.TransferOwnership(newOwner) } diff --git a/examples/gno.land/r/gnoland/monit/monit_test.gno b/examples/gno.land/r/gnoland/monit/monit_test.gno new file mode 100644 index 00000000000..fc9b394b8ed --- /dev/null +++ b/examples/gno.land/r/gnoland/monit/monit_test.gno @@ -0,0 +1,56 @@ +package monit + +import ( + "testing" + + "gno.land/p/demo/uassert" +) + +func TestPackage(t *testing.T) { + // initial state, watchdog is KO. + { + expected := `counter=0 +last update=0001-01-01 00:00:00 +0000 UTC +last caller= +status=KO` + got := Render("") + uassert.Equal(t, expected, got) + } + + // call Incr(), watchdog is OK. + Incr() + Incr() + Incr() + { + expected := `counter=3 +last update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 +last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +status=OK` + got := Render("") + uassert.Equal(t, expected, got) + } + + /* XXX: improve tests once we've the missing std.TestSkipTime feature + // wait 1h, watchdog is KO. + use std.TestSkipTime(time.Hour) + { + expected := `counter=3 + last update=2009-02-13 22:31:30 +0000 UTC m=+1234564290.000000001 + last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm + status=KO` + got := Render("") + uassert.Equal(t, expected, got) + } + + // call Incr(), watchdog is OK. + Incr() + { + expected := `counter=4 + last update=2009-02-13 23:31:30 +0000 UTC m=+1234567890.000000001 + last caller=g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm + status=OK` + got := Render("") + uassert.Equal(t, expected, got) + } + */ +}