title_clamp_filetest.gno
2.45 Kb · 64 lines
1// PKGPATH: gno.land/r/test/titleclamp
2package titleclamp
3
4// A proposal title is attacker-chosen (by a member) and is escaped on two
5// pages: the proposal page and the list page. Escaping costs about 6,990 gas a
6// byte, and the list page escapes one title per proposal shown, so an oversized
7// title priced the whole list out of the query cap: five proposals with 90 KB
8// titles cost 3,172,507,361 gas against a 3,000,000,000 cap. Clamping the title
9// before it is escaped brings the same five to 66,871,916.
10//
11// Runs in its own realm because it creates proposals, and the unit tests in the
12// impl package assert hard-coded proposal ids against shared state.
13
14import (
15 "strings"
16 "testing"
17
18 "gno.land/r/gov/dao"
19 "gno.land/r/gov/dao/impl/v0"
20 "gno.land/r/gov/dao/memberstore/v0"
21)
22
23const user address = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"
24
25func init(cur realm) {
26 memberstore.Get(0, cur).DeleteAll()
27 memberstore.Get(0, cur).SetTier(memberstore.T1)
28 memberstore.Get(0, cur).SetMember(memberstore.T1, user, memberstore.NewMember(3))
29 dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(impl.NewGovDAO(), nil))
30}
31
32func main(cur realm) {
33 testing.SetOriginCaller(user)
34 testing.SetRealm(testing.NewUserRealm(user))
35
36 short := dao.MustCreateProposal(cross(cur), dao.NewProposalRequest(
37 "An ordinary title", "d", nil))
38 long := dao.MustCreateProposal(cross(cur), dao.NewProposalRequest(
39 strings.Repeat("t", 50000), "d", nil))
40
41 // An ordinary title is untouched. Real titles in examples/ are about 40
42 // bytes, so the bound never reaches them.
43 sout := dao.Render(cross(cur), short.String())
44 println("an ordinary title is left alone:",
45 strings.Contains(sout, "An ordinary title") && !strings.Contains(sout, "… truncated"))
46
47 // The proposal page cuts the title before escaping it. Asserting the marker
48 // rather than a length: escaping first and cutting second would also
49 // produce a short page, so length alone cannot tell the two apart.
50 pout := dao.Render(cross(cur), long.String())
51 println("the proposal page clamps a huge title:",
52 strings.Contains(pout, "… truncated") && len(pout) < 4000)
53
54 // The list page escapes one title per proposal it shows, so it is the page
55 // the bound actually protects.
56 lout := dao.Render(cross(cur), "")
57 println("the list page clamps it too:",
58 strings.Contains(lout, "… truncated") && len(lout) < 4000)
59}
60
61// Output:
62// an ordinary title is left alone: true
63// the proposal page clamps a huge title: true
64// the list page clamps it too: true