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/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/arena/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd / gnofly / arena
Files
15 (gnomod.toml)
Exported functions
21
Module
gno.land/r/g1t2kg2vtr3fukg43eujkn6x53gfdyakhngt4sfd/gnofly/arena/v0
gno
0.9

Files (15)

  • gnomod.tomltoml
  • arena.gnogno
  • claims.gnogno
  • doc.gnogno
  • earnings.gnogno
  • errors.gnogno
  • escrow.gnogno
  • json.gnogno
  • lobby.gnogno
  • render.gnogno
  • results.gnogno
  • roles.gnogno
  • rounds.gnogno
  • spec.gnogno
  • validate.gnogno
  • lobby.gnogno
    1package arena23import (4	"chain"56	// Only for OriginSend, in Enter. Caller identity always comes from7	// cur.Previous(), never from this package.8

    Functions

    • ArenaJSON(id string) string

    • ArenasJSON() string

    • CancelRound(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}, id string)

    • Claim(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}) int64

    • Claimable(addr string) int64

    • ClaimsJSON(addr string) string

    • EarningsJSON() string

    • Enter(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}, spec string, ticket string) string

    • Escrowed() int64

    • ExpireLobby(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}, id string)

    • Leave(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}, id string)

    • PlayerJSON(addr string) string

    • RefundDelay() int64

    • RefundRound(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}, id string)

    • Render(path string) string

    • ResultJSON(id string) string

    • ResultsJSON() string

    • SetRefundDelay(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}, seconds int64)

    • SettleRound(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}, id string, deaths string)

    • StartRound(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}, id string) int64

    • Version() int64

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

    Rendered

    RenderedRawgnoweb ↗
    "chain/runtime/unsafe"
    9)
    10
    11// arena is one game: its spec, the players who paid and wait, and once it
    12// started, its round. It is stored from the first paid entry and deleted
    13// when its round is closed or its lobby empties.
    14type arena struct {
    15 spec
    16 lobby []address
    17 tickets []string // SHA-256 of each player's secret, 64 hex characters
    18 round *round // nil until the game starts
    19}
    20
    21func getArena(id string) *arena {
    22 v := arenas.Get(id)
    23 if v == nil {
    24 return nil
    25 }
    26 return v.(*arena)
    27}
    28
    29func mustArena(id string) *arena {
    30 a := getArena(id)
    31 if a == nil {
    32 panic(errUnknownArena)
    33 }
    34 return a
    35}
    36
    37// Enter puts the caller in the lobby of the arena that spec describes, and
    38// returns its id. The exact stake must be attached to the transaction
    39// (-send "<stake>ugnot") and stays in the realm until the player leaves or
    40// the game is over.
    41//
    42// The first entry creates the arena, the next ones join it: the realm
    43// recomputes the id from the spec every time. An arena takes entries for an
    44// hour after it was created, and until its game starts.
    45//
    46// ticket is 64 lowercase hex characters, the SHA-256 of a secret the
    47// player's client made up: the game server gives the seat to whoever shows
    48// the secret.
    49//
    50// It must be called directly by a user (maketx call): a call relayed by
    51// another realm or by a maketx run script is refused.
    52func Enter(cur realm, spec string, ticket string) string {
    53 player := callerOf(0, cur)
    54 // Paid-call guard: two checks that are only correct together.
    55 //
    56 // (a) The caller is a plain user account. Only then is the -send
    57 // envelope guaranteed to have landed at this realm's address. A
    58 // relaying realm or a maketx run script can attach coins to the
    59 // transaction and keep them, while OriginSend still reports them.
    60 if !cur.Previous().IsUserCall() {
    61 panic(errNotUserCall)
    62 }
    63 sp := parseSpec(spec)
    64 id := idOf(spec)
    65 t := now()
    66 if t > sp.created+entryWindow {
    67 panic(errEntryClosed)
    68 }
    69 if sp.created > t+clockSkew {
    70 panic(errNotYetCreated)
    71 }
    72 if !validTicket(ticket) {
    73 panic(errInvalidTicket)
    74 }
    75 a := getArena(id)
    76 if a == nil {
    77 a = &arena{spec: sp}
    78 }
    79 if a.round != nil {
    80 panic(errRoundRunning)
    81 }
    82 if indexOfAddr(a.lobby, player) >= 0 {
    83 panic(errAlreadyInLobby)
    84 }
    85 if int64(len(a.lobby)) >= a.maxSeats {
    86 panic(errLobbyFull)
    87 }
    88 // (b) The envelope is exactly the stake: one coin, the right denom, the
    89 // right amount. Anything else would either underpay or leave coins
    90 // in the realm that it owes to nobody.
    91 sent := unsafe.OriginSend()
    92 if len(sent) != 1 || sent[0].Denom != payDenom || sent[0].Amount != a.stake {
    93 panic("gnofly: wrong payment, send exactly " + itoa(a.stake) + payDenom)
    94 }
    95
    96 // The coins are already at the realm's address: the chain moved them
    97 // before the call. All that is left is to write down who they belong to.
    98 a.lobby = withAddr(a.lobby, player)
    99 a.tickets = withString(a.tickets, ticket)
    100 arenas.Set(id, a)
    101 hold(a.stake)
    102 // The guard above says the coins arrived. This looks at the balance.
    103 assertSolvent(0, cur)
    104
    105 chain.Emit("Entered",
    106 "arena", id,
    107 "player", player.String(),
    108 "stake", itoa(a.stake),
    109 "ticket", ticket,
    110 "lobby", itoa(int64(len(a.lobby))),
    111 )
    112 return id
    113}
    114
    115// Leave takes the caller out of the lobby of an arena and sends their stake
    116// back. It works until the game starts. The last player out deletes the
    117// arena: nothing is left on chain of a game nobody played.
    118func Leave(cur realm, id string) {
    119 player := callerOf(0, cur)
    120 a := mustArena(id)
    121 seat := indexOfAddr(a.lobby, player)
    122 if seat < 0 {
    123 panic(errNotInLobby)
    124 }
    125 a.lobby = withoutAddr(a.lobby, seat)
    126 a.tickets = withoutString(a.tickets, seat)
    127 if len(a.lobby) == 0 {
    128 arenas.Remove(id)
    129 }
    130 release(a.stake)
    131
    132 pay(0, cur, player, a.stake)
    133 assertSolvent(0, cur)
    134
    135 chain.Emit("Left",
    136 "arena", id,
    137 "player", player.String(),
    138 "stake", itoa(a.stake),
    139 )
    140}
    141
    142// ExpireLobby closes an arena whose game never started: every player in the
    143// lobby gets their stake back, and the arena is deleted. Anyone can call it
    144// once the arena stopped taking entries, an hour after it was created.
    145func ExpireLobby(cur realm, id string) {
    146 caller := callerOf(0, cur)
    147 a := mustArena(id)
    148 if a.round != nil {
    149 panic(errRoundRunning)
    150 }
    151 if now() <= a.created+entryWindow {
    152 panic(errNotExpired)
    153 }
    154 players := a.lobby
    155 arenas.Remove(id)
    156 release(a.stake * int64(len(players)))
    157 for _, p := range players {
    158 pay(0, cur, p, a.stake)
    159 }
    160 assertSolvent(0, cur)
    161
    162 chain.Emit("LobbyExpired",
    163 "arena", id,
    164 "players", itoa(int64(len(players))),
    165 "caller", caller.String(),
    166 )
    167}
    168
    169// The four helpers below return a new slice of exactly the length of its
    170// content, so that nothing else stays in storage. They return nil for an
    171// empty list.
    172
    173// withAddr returns list with v added at the end.
    174func withAddr(list []address, v address) []address {
    175 out := make([]address, len(list), len(list)+1)
    176 copy(out, list)
    177 return append(out, v)
    178}
    179
    180// withString returns list with v added at the end.
    181func withString(list []string, v string) []string {
    182 out := make([]string, len(list), len(list)+1)
    183 copy(out, list)
    184 return append(out, v)
    185}
    186
    187// withoutAddr returns list without the element at index at.
    188func withoutAddr(list []address, at int) []address {
    189 if len(list) == 1 {
    190 return nil
    191 }
    192 out := make([]address, 0, len(list)-1)
    193 out = append(out, list[:at]...)
    194 return append(out, list[at+1:]...)
    195}
    196
    197// withoutString returns list without the element at index at.
    198func withoutString(list []string, at int) []string {
    199 if len(list) == 1 {
    200 return nil
    201 }
    202 out := make([]string, 0, len(list)-1)
    203 out = append(out, list[:at]...)
    204 return append(out, list[at+1:]...)
    205}
    206

    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.