Transaction
6302BE62F6AF85…CA03E02CF9C7
Block 654,258 · index 0 · RPC
Summary
- Hash
- 6302BE62F6AF85EC5F66B67D18F875055607961FA97AFBF95FABCA03E02CF9C7
- Block
- 654,258
- Size
- 10858 bytes
- Gas used
- 19,424,612 / 100,000,000
- Fee
- 10000000 ugnot
- Status
- success
Messages
Arguments · 9
- #1telescope
- #2gnomod.toml
- #3module = "gno.land/r/nym-vikbez000/telescope" gno = "0.9" private = true
- #4telescope.gno
- #5package telescope import ( "chain" "strconv" ns "gno.land/p/nym-vikbez000/nightsky/v1" registry "gno.land/r/nym-vikbez000/nightsky/v1" ) var telescope *ns.TelescopeRealm func init(cur realm) { owner := "g13kytw9mpyutwmyg5eq7arqxqcszfl6uq4p89zg" config := ns.NewTelescopeConfig( "vik telescope", // name "Seestar S30", // model // Do not use too precise coordinates to keep some privacy 49.02, // latitude 1.15, // longitude owner, cur.PkgPath(), ) telescope = ns.NewTelescopeRealm(&config) telescope.Config.UpdateStatus("online") registry.Register(cross(cur), config) } func Render(path string) string { return telescope.RenderNightsky(path) } // Identity note: access control is keyed by the immediate caller via // cur.Previous().Address() — the statically-scoped realm handle, not the // stack-walking tx-origin. For a direct `gnokey maketx call` this resolves to // the signing EOA (matching the address-keyed access list); a call routed // through an intermediary realm is identified as that realm and denied unless // explicitly granted. This is the phishing-resistant pattern recommended in // gno-interrealm.md, and avoids the chain/runtime/unsafe primitives entirely. // SubmitCommand allows authorized users to submit telescope commands. // commandType: "capture" or "stop" // targetRA: Right Ascension in hours (0-24), leave empty for stop // targetDec: Declination in degrees (-90 to 90), leave empty for stop // exposure: Exposure time in seconds (1-300), leave empty for stop func SubmitCommand(cur realm, commandType, targetRA, targetDec, exposure string) { caller := cur.Previous().Address().String() var ra, dec float64 var exp int if commandType == "capture" { // Reject malformed numbers instead of silently treating them as 0, // which would otherwise pass coordinate validation. var err error if ra, err = strconv.ParseFloat(targetRA, 64); err != nil { panic("invalid RA: must be a number") } if dec, err = strconv.ParseFloat(targetDec, 64); err != nil { panic("invalid Dec: must be a number") } if exp, err = strconv.Atoi(exposure); err != nil { panic("invalid exposure: must be an integer") } } telescope.SubmitCommand(caller, commandType, ra, dec, exp) chain.Emit("CommandSubmitted", "type", commandType, "requester", caller) } // GrantAccess allows the owner to grant access to another user. // durationDays: 0 = never expires func GrantAccess(cur realm, address string, durationDays int) { caller := cur.Previous().Address().String() telescope.GrantAccess(caller, address, durationDays) chain.Emit("AccessGranted", "address", address, "durationDays", strconv.Itoa(durationDays)) } // RevokeAccess allows the owner to revoke access from a user. func RevokeAccess(cur realm, address string) { caller := cur.Previous().Address().String() telescope.RevokeAccess(caller, address) chain.Emit("AccessRevoked", "address", address) } // GetNextCommand returns and removes the next command from the queue. // Called by the telescope controller. func GetNextCommand(cur realm) ns.TelescopeCommand { caller := cur.Previous().Address().String() return telescope.GetNextCommand(caller) } // UpdateStatus updates the telescope status locally and in the NightSky registry. // Valid values: "online", "offline", "busy", "error" func UpdateStatus(cur realm, status string) { switch status { case "online", "offline", "busy", "error": default: panic("invalid status: must be online, offline, busy, or error") } caller := cur.Previous().Address().String() telescope.UpdateStatus(caller, status) registry.UpdateTelescopeStatus(cross(cur), status) } // RecordCapture records a capture result locally and submits it to the NightSky network feed. func RecordCapture(cur realm, imageURL string, targetRA, targetDec float64, exposure int, capturedBy string) { caller := cur.Previous().Address().String() telescope.RecordCapture(caller, imageURL, targetRA, targetDec, exposure, capturedBy) registry.SubmitCapture(cross(cur), imageURL, targetRA, targetDec, exposure, capturedBy) chain.Emit("CaptureRecorded", "imageURL", imageURL, "capturedBy", capturedBy) } // ClearCommandQueue clears all pending commands (emergency stop). func ClearCommandQueue(cur realm) { caller := cur.Previous().Address().String() telescope.ClearCommandQueue(caller) chain.Emit("CommandQueueCleared", "by", caller) } func GetCommandCount() int { return telescope.GetCommandCount() } func GetCaptureCount() int { return telescope.GetCaptureCount() } func GetAccessRuleCount() int { return telescope.GetAccessRuleCount() } func GetStatus() string { return telescope.Config.Status } func GetTelescopeName() string { return telescope.Config.Name } func GetTelescopeModel() string { return telescope.Config.Model } func GetOwner() string { return telescope.OwnerAddress }
- #6telescope_test.gno
- #7package telescope import ( "testing" "gno.land/p/nt/testutils/v0" "gno.land/p/nt/uassert/v0" ) // ownerAddr must match the owner configured in telescope.gno's init(). var ownerAddr = address("g13kytw9mpyutwmyg5eq7arqxqcszfl6uq4p89zg") func TestSubmitCommandValid(cur realm, t *testing.T) { telescope.CommandQueue = nil testing.SetRealm(testing.NewUserRealm(ownerAddr)) SubmitCommand(cross(cur), "capture", "5.5", "22.5", "60") uassert.Equal(t, 1, GetCommandCount()) uassert.Equal(t, "busy", GetStatus()) } func TestSubmitCommandRejectsMalformedNumber(cur realm, t *testing.T) { testing.SetRealm(testing.NewUserRealm(ownerAddr)) // A non-numeric RA must be rejected, not silently coerced to 0. uassert.AbortsContains(t, cur, "invalid RA", func() { SubmitCommand(cross(cur), "capture", "notanumber", "22.5", "60") }) } func TestSubmitCommandAccessDenied(cur realm, t *testing.T) { // A stranger with no grant cannot submit. testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("stranger"))) uassert.AbortsContains(t, cur, "access denied", func() { SubmitCommand(cross(cur), "capture", "5.5", "22.5", "60") }) } func TestGrantThenSubmit(cur realm, t *testing.T) { telescope.CommandQueue = nil friend := testutils.TestAddress("friend") // Owner grants access to the friend. testing.SetRealm(testing.NewUserRealm(ownerAddr)) GrantAccess(cross(cur), friend.String(), 0) uassert.Equal(t, 1, GetAccessRuleCount()) // Friend can now submit a stop command. testing.SetRealm(testing.NewUserRealm(friend)) SubmitCommand(cross(cur), "stop", "", "", "") uassert.Equal(t, 1, GetCommandCount()) // Owner revokes; friend is denied again. testing.SetRealm(testing.NewUserRealm(ownerAddr)) RevokeAccess(cross(cur), friend.String()) uassert.Equal(t, 0, GetAccessRuleCount()) testing.SetRealm(testing.NewUserRealm(friend)) uassert.AbortsContains(t, cur, "access denied", func() { SubmitCommand(cross(cur), "stop", "", "", "") }) } // --- Adversarial ("steal a telescope") tests ------------------------------- // An attacker tries to take over or misuse a telescope they do not own. Every // privileged path must reject them, and ownership must be unchanged afterwards. // TestStrangerCannotControlTelescope: an EOA with no relationship to the // telescope cannot use or control it through any entrypoint. func TestStrangerCannotControlTelescope(cur realm, t *testing.T) { intruder := testutils.TestAddress("intruder") testing.SetRealm(testing.NewUserRealm(intruder)) // Use is denied (no access grant). uassert.AbortsContains(t, cur, "access denied", func() { SubmitCommand(cross(cur), "capture", "5", "5", "30") }) // Every owner-only entrypoint is denied. uassert.AbortsContains(t, cur, "owner only", func() { GrantAccess(cross(cur), intruder.String(), 0) // grant self }) uassert.AbortsContains(t, cur, "owner only", func() { RevokeAccess(cross(cur), ownerAddr.String()) // revoke the real owner }) uassert.AbortsContains(t, cur, "owner only", func() { GetNextCommand(cross(cur)) // drain the queue }) uassert.AbortsContains(t, cur, "owner only", func() { ClearCommandQueue(cross(cur)) }) uassert.AbortsContains(t, cur, "owner only", func() { UpdateStatus(cross(cur), "offline") }) uassert.AbortsContains(t, cur, "owner only", func() { RecordCapture(cross(cur), "https://evil/forged.png", 1, 1, 10, intruder.String()) }) // The telescope still belongs to its owner. uassert.Equal(t, ownerAddr.String(), GetOwner()) } // TestAccessIsNotOwnership: a user who has been granted *access* can use the // telescope, but cannot escalate to control it. This is the core "steal" // scenario — proving a granted friend can't quietly take over. func TestAccessIsNotOwnership(cur realm, t *testing.T) { friend := testutils.TestAddress("friend") accomplice := testutils.TestAddress("accomplice") // Owner grants the friend use-access. testing.SetRealm(testing.NewUserRealm(ownerAddr)) GrantAccess(cross(cur), friend.String(), 0) // The friend can submit commands (legitimate use)... telescope.CommandQueue = nil testing.SetRealm(testing.NewUserRealm(friend)) SubmitCommand(cross(cur), "stop", "", "", "") uassert.Equal(t, 1, GetCommandCount()) // ...but cannot perform any owner-only action to take control. uassert.AbortsContains(t, cur, "owner only", func() { GrantAccess(cross(cur), accomplice.String(), 0) // invite an accomplice }) uassert.AbortsContains(t, cur, "owner only", func() { RevokeAccess(cross(cur), ownerAddr.String()) // lock out the owner }) uassert.AbortsContains(t, cur, "owner only", func() { GetNextCommand(cross(cur)) // read/consume the queue }) uassert.AbortsContains(t, cur, "owner only", func() { ClearCommandQueue(cross(cur)) }) uassert.AbortsContains(t, cur, "owner only", func() { UpdateStatus(cross(cur), "error") }) uassert.AbortsContains(t, cur, "owner only", func() { RecordCapture(cross(cur), "https://evil/forged.png", 1, 1, 10, friend.String()) }) // The accomplice was never granted access, so they remain locked out. testing.SetRealm(testing.NewUserRealm(accomplice)) uassert.AbortsContains(t, cur, "access denied", func() { SubmitCommand(cross(cur), "stop", "", "", "") }) // Cleanup: owner revokes the grant and clears the queue. testing.SetRealm(testing.NewUserRealm(ownerAddr)) RevokeAccess(cross(cur), friend.String()) telescope.CommandQueue = nil // Ownership unchanged throughout. uassert.Equal(t, ownerAddr.String(), GetOwner()) }
- #8/gno.MemPackageType
- #9 MPUserAll
Result log
msg:0,success:true,log:,events:[]