lock.gno
2.47 Kb · 78 lines
1package arena
2
3import (
4 "chain/runtime"
5 "strconv"
6 "time"
7)
8
9// guard binds the session that signed the transaction to match id, held in m, saving the
10// caller's record only when its locks change.
11func guard(fromUser bool, caller address, m record, id int) {
12 session, expires, ok := signingSession(fromUser)
13 if !ok {
14 return
15 }
16 if caller != m.challenger && caller != m.acceptor {
17 panic("not a player")
18 }
19 p := loadPlayer(caller)
20 if p.lock(session, expires, id, false, time.Now().Unix()) {
21 savePlayer(caller, p)
22 }
23}
24
25// join adds match id to the caller's record and binds the session that signed the
26// transaction to it, refused when that session is bound to a match already.
27func join(fromUser bool, caller address, id int) {
28 p := loadPlayer(caller)
29 if session, expires, ok := signingSession(fromUser); ok {
30 p.lock(session, expires, id, true, time.Now().Unix())
31 }
32 p.add(id)
33 savePlayer(caller, p)
34}
35
36// signingSession returns the signing session key and its expiry when fromUser: a session
37// signs the whole transaction, so a call through another realm is left unchecked.
38func signingSession(fromUser bool) (address, int64, bool) {
39 if !fromUser {
40 return "", 0, false
41 }
42 session, expires, _, ok := runtime.GetSessionInfo()
43 return session, expires, ok
44}
45
46// lock binds session to match id in p's locks, panicking when it is refused, and reports
47// whether the locks changed.
48func (p *playerRecord) lock(session address, expires int64, id int, opening bool, now int64) bool {
49 locks, changed, refusal := lockSession(p.locks, session, expires, id, opening, now)
50 if refusal != "" {
51 panic(refusal)
52 }
53 p.locks = locks
54 return changed
55}
56
57// lockSession binds session to match id, refused when bound to another, or any when opening.
58// It drops expired locks and any lock its key holds under another expiry (Unix seconds, 0 never).
59func lockSession(locks []sessionLock, session address, expires int64, id int, opening bool, now int64) (kept []sessionLock, changed bool, refusal string) {
60 for _, l := range locks {
61 expired := l.expires != 0 && l.expires <= now
62 stale := l.session == session && l.expires != expires
63 if !expired && !stale {
64 kept = append(kept, l)
65 }
66 }
67 changed = len(kept) != len(locks)
68 for _, l := range kept {
69 if l.session != session {
70 continue
71 }
72 if opening || l.id != id {
73 return kept, changed, "session locked to match " + strconv.Itoa(l.id)
74 }
75 return kept, changed, ""
76 }
77 return append(kept, sessionLock{session: session, id: id, expires: expires}), true, ""
78}