PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/moul/agents/jury/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
moul / agents / jury
Files
4 (README)(gnomod.toml)
Exported functions
7
Module
gno.land/r/moul/agents/jury/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • jury.gnogno
  • jury_test.gnogno
jury.gnogno
1// Package jury is a commit-reveal adversarial review protocol for disputes2// over agent work.3//4// The hard part of an agent economy is not paying agents — it is deciding5// what happens when an agent's output is disputed. This realm runs a small6// jury: a fixed panel of reviewers each *commit* to a hidden verdict, then7// *reveal* it. Commit-reveal means no juror can copy another's vote or be8// swayed after seeing the tally; every verdict is locked in blind.9//10// The subject of a case is an opaque string — a receipt sequence, a claim11// id, an off-chain artifact hash — so this realm composes with the receipt12// and gnomem demos without depending on them.13//14// Bonding and slashing (a juror stakes coins, loses them for provably bad15// verdicts) are the natural economic layer on top; this demo shows the16// mechanism without the money.17package jury1819import (20	"chain"21	"chain/runtime"22	"strings"2324	"gno.land/p/moul/agents/commit/v0"25	"gno.land/p/nt/ufmt/v0"26)2728// Phase is a case's position in the commit-reveal lifecycle.29type Phase string3031const (32	PhaseCommit Phase = "commit"33	PhaseReveal Phase = "reveal"34	PhaseClosed Phase = "closed"35)3637// Juror is one panelist and their (eventually revealed) verdict.38type Juror struct {39	Addr       address40	Commitment string // commit.Verdict(verdict, salt)41	Committed  bool42	Revealed   bool43	Verdict    bool // meaningful once Revealed44	Height     int6445}4647// Case is a single dispute under review by a fixed panel.48type Case struct {49	ID       uint6450	Subject  string // opaque reference to the disputed thing51	Opener   address52	Phase    Phase53	Jurors   []*Juror54	OpenedAt int6455	Outcome  string // "upheld" | "rejected" | "tie" once closed56	Yes      int57	No       int58}5960var cases []*Case // index 0 => ID 16162// OpenCase starts a dispute with a fixed panel of jurors. An odd panel size63// avoids ties; duplicates are rejected.64func OpenCase(cur realm, subject string, jurors []address) uint64 {65	assert(subject != "", "empty subject")66	assert(len(jurors) >= 1, "need at least one juror")6768	seen := make(map[string]bool)69	panel := make([]*Juror, 0, len(jurors))70	for _, j := range jurors {71		key := j.String()72		assert(!seen[key], "duplicate juror: "+key)73		seen[key] = true74		panel = append(panel, &Juror{Addr: j})75	}7677	id := uint64(len(cases)) + 178	cases = append(cases, &Case{79		ID:       id,80		Subject:  subject,81		Opener:   cur.Previous().Address(),82		Phase:    PhaseCommit,83		Jurors:   panel,84		OpenedAt: runtime.ChainHeight(),85	})86	chain.Emit("CaseOpened", "id", ufmt.Sprintf("%d", id), "jurors", ufmt.Sprintf("%d", len(panel)))87	return id88}8990// Commit locks in a juror's hidden verdict. commitment must equal91// commit.Verdict(verdict, salt) — computed off-chain. When the last juror92// commits, the case advances to the reveal phase automatically.93func Commit(cur realm, caseID uint64, commitment string) {94	c := mustCase(caseID)95	assert(c.Phase == PhaseCommit, "not in commit phase")96	assert(commitment != "", "empty commitment")97	j := mustJuror(c, cur.Previous().Address())98	assert(!j.Committed, "already committed")99	j.Commitment = commitment100	j.Committed = true101	j.Height = runtime.ChainHeight()102103	if allCommitted(c) {104		c.Phase = PhaseReveal105		chain.Emit("CasePhase", "id", ufmt.Sprintf("%d", caseID), "phase", string(PhaseReveal))106	}107}108109// Reveal opens a juror's verdict. The (verdict, salt) must reproduce the110// commitment made earlier, or the reveal is rejected. When the last juror111// reveals, the case closes and the majority outcome is recorded.112func Reveal(cur realm, caseID uint64, verdict bool, salt string) {113	c := mustCase(caseID)114	assert(c.Phase == PhaseReveal, "not in reveal phase")115	j := mustJuror(c, cur.Previous().Address())116	assert(j.Committed && !j.Revealed, "nothing to reveal")117	assert(commit.Verdict(verdict, salt) == j.Commitment, "reveal does not match commitment")118119	j.Revealed = true120	j.Verdict = verdict121	if verdict {122		c.Yes++123	} else {124		c.No++125	}126	chain.Emit("VerdictRevealed", "id", ufmt.Sprintf("%d", caseID), "verdict", boolStr(verdict))127128	if allRevealed(c) {129		closeCase(c)130	}131}132133// ---- read-only API ----134135// Get returns a copy of a case (jurors flattened separately via Jury).136func Get(caseID uint64) Case { return *mustCase(caseID) }137138// Outcome returns the recorded outcome, or "" if the case is still open.139func Outcome(caseID uint64) string { return mustCase(caseID).Outcome }140141// Count returns the number of cases.142func Count() int { return len(cases) }143144// ---- internal ----145146func closeCase(c *Case) {147	switch {148	case c.Yes > c.No:149		c.Outcome = "upheld"150	case c.No > c.Yes:151		c.Outcome = "rejected"152	default:153		c.Outcome = "tie"154	}155	c.Phase = PhaseClosed156	chain.Emit("CaseClosed", "id", ufmt.Sprintf("%d", c.ID), "outcome", c.Outcome)157}158159func allCommitted(c *Case) bool {160	for _, j := range c.Jurors {161		if !j.Committed {162			return false163		}164	}165	return true166}167168func allRevealed(c *Case) bool {169	for _, j := range c.Jurors {170		if !j.Revealed {171			return false172		}173	}174	return true175}176177func mustCase(id uint64) *Case {178	assert(id >= 1 && id <= uint64(len(cases)), "unknown case")179	return cases[id-1]180}181182func mustJuror(c *Case, addr address) *Juror {183	for _, j := range c.Jurors {184		if j.Addr == addr {185			return j186		}187	}188	panic("caller is not on this jury")189}190191func boolStr(b bool) string {192	if b {193		return "yes"194	}195	return "no"196}197198func assert(cond bool, msg string) {199	if !cond {200		panic(msg)201	}202}203204// Render shows all cases, or one case's panel + tally at :<id>.205func Render(path string) string {206	if path == "" {207		return renderIndex()208	}209	return renderCase(parseID(path))210}211212func renderIndex() string {213	var sb strings.Builder214	sb.WriteString("# Agent Jury\n\n")215	sb.WriteString("_Who checks the agents?_ Blind commit-reveal review by a fixed panel.\n\n")216	if len(cases) == 0 {217		sb.WriteString("_No cases yet._\n")218		return sb.String()219	}220	sb.WriteString("| # | Subject | Phase | ✅ | ❌ | Outcome |\n")221	sb.WriteString("|---|---|---|---|---|---|\n")222	for _, c := range cases {223		out := c.Outcome224		if out == "" {225			out = "—"226		}227		sb.WriteString(ufmt.Sprintf("| [%d](/r/moul/agents/jury/v0:%d) | %s | %s | %d | %d | %s |\n",228			c.ID, c.ID, c.Subject, string(c.Phase), c.Yes, c.No, out))229	}230	return sb.String()231}232233func renderCase(id uint64) string {234	c := mustCase(id)235	var sb strings.Builder236	sb.WriteString(ufmt.Sprintf("# Case #%d\n\n", c.ID))237	sb.WriteString(ufmt.Sprintf("- **Subject:** %s\n", c.Subject))238	sb.WriteString(ufmt.Sprintf("- **Phase:** %s\n", string(c.Phase)))239	sb.WriteString(ufmt.Sprintf("- **Opened by:** %s at height %d\n", short(c.Opener), c.OpenedAt))240	if c.Outcome != "" {241		sb.WriteString(ufmt.Sprintf("- **Outcome:** **%s** (%d ✅ / %d ❌)\n", c.Outcome, c.Yes, c.No))242	}243	sb.WriteString("\n## Panel\n\n")244	sb.WriteString("| Juror | Committed | Revealed | Verdict | Dissent |\n|---|---|---|---|---|\n")245	majority := c.Yes >= c.No // for dissent marking once closed246	for _, j := range c.Jurors {247		verdict, dissent := "—", ""248		if j.Revealed {249			verdict = boolStr(j.Verdict)250			if c.Phase == PhaseClosed && c.Outcome != "tie" && j.Verdict != majority {251				dissent = "⚠"252			}253		}254		sb.WriteString(ufmt.Sprintf("| %s | %t | %t | %s | %s |\n",255			short(j.Addr), j.Committed, j.Revealed, verdict, dissent))256	}257	return sb.String()258}259260func parseID(s string) uint64 {261	var n uint64262	for i := 0; i < len(s); i++ {263		ch := s[i]264		assert(ch >= '0' && ch <= '9', "invalid case path: "+s)265		n = n*10 + uint64(ch-'0')266	}267	return n268}269270func short(a address) string {271	s := a.String()272	if len(s) <= 12 {273		return s274	}275	return s[:8] + "…" + s[len(s)-4:]276}277

Functions

  • Commit(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, caseID uint64, commitment string)

  • Count() int

  • Get(caseID uint64) struct{ID uint64; Subject string; Opener .uverse.address; Phase gno.land/r/moul/agents/jury/v0.Phase; Jurors []*gno.land/r/moul/agents/jury/v0.Juror; OpenedAt int64; Outcome string; Yes int; No int}

  • OpenCase(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, subject string, jurors [].uverse.address) uint64

  • Outcome(caseID uint64) string

  • Render(path string) string

  • Reveal(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, caseID uint64, verdict bool, salt string)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.