contract.gno
3.71 Kb ยท 152 lines
1package ghverify
2
3import (
4 "chain"
5 "chain/runtime"
6 "errors"
7
8 "gno.land/p/demo/gnorkle/feeds/static"
9 "gno.land/p/demo/gnorkle/gnorkle"
10 "gno.land/p/demo/gnorkle/message"
11 "gno.land/p/nt/avl"
12)
13
14const (
15 // The agent should send this value if it has verified the github handle.
16 verifiedResult = "OK"
17)
18
19var (
20 ownerAddress = runtime.OriginCaller()
21 oracle *gnorkle.Instance
22 postHandler postGnorkleMessageHandler
23
24 handleToAddressMap = avl.NewTree()
25 addressToHandleMap = avl.NewTree()
26)
27
28func init() {
29 oracle = gnorkle.NewInstance()
30 oracle.AddToWhitelist("", []string{string(ownerAddress)})
31}
32
33type postGnorkleMessageHandler struct{}
34
35// Handle does post processing after a message is ingested by the oracle feed. It extracts the value to realm
36// storage and removes the feed from the oracle.
37func (h postGnorkleMessageHandler) Handle(i *gnorkle.Instance, funcType message.FuncType, feed gnorkle.Feed) error {
38 if funcType != message.FuncTypeIngest {
39 return nil
40 }
41
42 result, _, consumable := feed.Value()
43 if !consumable {
44 return nil
45 }
46
47 // The value is consumable, meaning the ingestion occurred, so we can remove the feed from the oracle
48 // after saving it to realm storage.
49 defer oracle.RemoveFeed(feed.ID())
50
51 // Couldn't verify; nothing to do.
52 if result.String != verifiedResult {
53 return nil
54 }
55
56 feedTasks := feed.Tasks()
57 if len(feedTasks) != 1 {
58 return errors.New("expected feed to have exactly one task")
59 }
60
61 task, ok := feedTasks[0].(*verificationTask)
62 if !ok {
63 return errors.New("expected ghverify task")
64 }
65
66 handleToAddressMap.Set(task.githubHandle, task.gnoAddress)
67 addressToHandleMap.Set(task.gnoAddress, task.githubHandle)
68 return nil
69}
70
71// RequestVerification creates a new static feed with a single task that will
72// instruct an agent to verify the github handle / gno address pair.
73func RequestVerification(cur realm, githubHandle string) {
74 gnoAddress := string(runtime.OriginCaller())
75 if err := oracle.AddFeeds(
76 static.NewSingleValueFeed(
77 gnoAddress,
78 "string",
79 &verificationTask{
80 gnoAddress: gnoAddress,
81 githubHandle: githubHandle,
82 },
83 ),
84 ); err != nil {
85 panic(err)
86 }
87 chain.Emit(
88 "verification_requested",
89 "from", gnoAddress,
90 "handle", githubHandle,
91 )
92}
93
94// GnorkleEntrypoint is the entrypoint to the gnorkle oracle handler.
95func GnorkleEntrypoint(cur realm, message string) string {
96 result, err := oracle.HandleMessage(message, postHandler)
97 if err != nil {
98 panic(err)
99 }
100
101 return result
102}
103
104// SetOwner transfers ownership of the contract to the given address.
105func SetOwner(_ realm, owner address) {
106 if ownerAddress != runtime.OriginCaller() {
107 panic("only the owner can set a new owner")
108 }
109
110 ownerAddress = owner
111
112 // In the context of this contract, the owner is the only one that can
113 // add new feeds to the oracle.
114 oracle.ClearWhitelist("")
115 oracle.AddToWhitelist("", []string{string(ownerAddress)})
116}
117
118// GetHandleByAddress returns the github handle associated with the given gno address.
119func GetHandleByAddress(cur realm, address_XXX string) string {
120 if value, ok := addressToHandleMap.Get(address_XXX); ok {
121 return value.(string)
122 }
123
124 return ""
125}
126
127// GetAddressByHandle returns the gno address associated with the given github handle.
128func GetAddressByHandle(cur realm, handle string) string {
129 if value, ok := handleToAddressMap.Get(handle); ok {
130 return value.(string)
131 }
132
133 return ""
134}
135
136// Render returns a json object string will all verified handle -> address mappings.
137func Render(_ string) string {
138 result := "{"
139 var appendComma bool
140 handleToAddressMap.Iterate("", "", func(handle string, address_XXX any) bool {
141 if appendComma {
142 result += ","
143 }
144
145 result += `"` + handle + `": "` + address_XXX.(string) + `"`
146 appendComma = true
147
148 return false
149 })
150
151 return result + "}"
152}