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