notes.gno
4.49 Kb · 150 lines
1package memba_notes_v1
2
3import (
4 "chain"
5 "chain/banker"
6 "chain/runtime"
7)
8
9// CreateNote is the only payable entrypoint; encrypted creation is atomic.
10func CreateNote(cur realm, id [16]byte, mode uint8, title, body, access, commitment []byte, maxFee int64, opID [16]byte) {
11 who := enter(cur, true)
12 defer leave()
13 if !cur.Previous().IsUserCall() {
14 panic("notes: create directly from your account")
15 }
16 if paused {
17 panic("notes: creation paused")
18 }
19 if id == ([16]byte{}) {
20 panic("notes: empty note id")
21 }
22 if _, ok := notes.Get(idText(id)); ok {
23 panic("notes: note id used")
24 }
25 if mode > ModePublicOpen || public(mode) && (len(access) != 0 || len(commitment) != 0) {
26 panic("notes: unsupported creation mode")
27 }
28 if public(mode) {
29 validatePublic(title, body)
30 }
31 operation(nil, opID)
32 assertCreatePayment(cur, maxFee)
33 if sequence == ^uint64(0) {
34 panic("notes: sequence exhausted")
35 }
36 sequence++
37 n := ¬e{ID: id, Owner: who, OwnerGeneration: 1, Mode: mode, TitleRevision: 1, BodyRevision: 1, Title: copied(title), Body: copied(body), BodyBytes: len(body), CreatedHeight: runtime.ChainHeight(), Sequence: sequence, Listed: public(mode)}
38 if encrypted(mode) {
39 initializeEncrypted(n, access, commitment)
40 }
41 bump(n, who, opID, "create")
42 notes.Set(idText(id), n)
43 index(n)
44 if createFee > 0 {
45 banker.NewBanker(banker.BankerTypeRealmSend, cur).SendCoins(cur.Address(), treasury, chain.Coins{chain.NewCoin("ugnot", createFee)})
46 }
47}
48
49// fieldMask: 1 title, 2 body, 3 both. An unchanged field must be omitted (empty
50// blob). The separate field revision advances only when that field changes.
51func Commit(cur realm, id [16]byte, baseRevision uint64, expectedEpoch uint32, fieldMask uint8, title, body []byte, opID [16]byte) {
52 who := enter(cur, false)
53 defer leave()
54 mutateCommit(cur, who, id, baseRevision, expectedEpoch, fieldMask, title, body, opID)
55}
56func mutateCommit(cur realm, who address, id [16]byte, baseRevision uint64, expectedEpoch uint32, fieldMask uint8, title, body []byte, opID [16]byte) {
57 n := live(id)
58 requireContentWrite(cur, n, who)
59 expected(n, baseRevision)
60 if n.Epoch != expectedEpoch {
61 panic("notes: stale epoch")
62 }
63 privateWritable(n)
64 if fieldMask < 1 || fieldMask > 3 || (fieldMask&1 == 0 && len(title) != 0) || (fieldMask&2 == 0 && len(body) != 0) {
65 panic("notes: bad field mask")
66 }
67 if fieldMask&1 != 0 {
68 if public(n.Mode) {
69 validateTitle(title)
70 } else {
71 validateCipher(title, n.Epoch, 1, n.TitleRevision+1, MaxCipherTitleBytes)
72 }
73 if n.TitleRevision == ^uint64(0) {
74 panic("notes: title revision exhausted")
75 }
76 }
77 if fieldMask&2 != 0 {
78 if public(n.Mode) {
79 validatePublic(n.Title, body)
80 } else {
81 validateCipher(body, n.Epoch, 2, n.BodyRevision+1, MaxCipherBodyBytes)
82 }
83 if n.BodyRevision == ^uint64(0) {
84 panic("notes: body revision exhausted")
85 }
86 }
87 operation(n, opID)
88 if fieldMask&1 != 0 {
89 n.Title = copied(title)
90 n.TitleRevision++
91 }
92 if fieldMask&2 != 0 {
93 n.Body = copied(body)
94 n.BodyBytes = len(body)
95 n.BodyRevision++
96 }
97 bump(n, who, opID, "commit")
98}
99func Rename(cur realm, id [16]byte, baseRevision uint64, expectedEpoch uint32, title []byte, opID [16]byte) {
100 Commit(cur, id, baseRevision, expectedEpoch, 1, title, nil, opID)
101}
102
103// Delete retains the ID and last operation receipt forever. Large collections
104// are purged by bounded follow-up calls, never by an unbounded delete loop.
105func Delete(cur realm, id [16]byte, baseRevision uint64, opID [16]byte) {
106 who := enter(cur, false)
107 defer leave()
108 mutateDelete(cur, who, id, baseRevision, opID)
109}
110func mutateDelete(cur realm, who address, id [16]byte, baseRevision uint64, opID [16]byte) {
111 n := live(id)
112 owner(n, who)
113 expected(n, baseRevision)
114 operation(n, opID)
115 unindex(n)
116 resetPublicWrites(n)
117 n.Deleted = true
118 n.Listed = false
119 n.Title = nil
120 n.Body = nil
121 n.BodyBytes = 0
122 n.Commitment = nil
123 n.PendingOwner = ""
124 n.Writers = nil
125 n.Readers = nil
126 n.Policies = [3]string{}
127 n.GovWriters = false
128 bump(n, who, opID, "delete")
129}
130func SetCommentMode(cur realm, id [16]byte, baseRevision uint64, open bool, opID [16]byte) {
131 who := enter(cur, false)
132 defer leave()
133 mutateSetCommentMode(cur, who, id, baseRevision, open, opID)
134}
135func mutateSetCommentMode(cur realm, who address, id [16]byte, baseRevision uint64, open bool, opID [16]byte) {
136 n := live(id)
137 owner(n, who)
138 expected(n, baseRevision)
139 if !public(n.Mode) {
140 panic("notes: public note required")
141 }
142 operation(n, opID)
143 if open {
144 n.Mode = ModePublicOpen
145 } else {
146 resetPublicWrites(n)
147 n.Mode = ModePublic
148 }
149 bump(n, who, opID, "comments-mode")
150}