contract.gno

3.68 Kb ยท 151 lines
  1package ghverify
  2
  3import (
  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
 13const (
 14	// The agent should send this value if it has verified the github handle.
 15	verifiedResult = "OK"
 16)
 17
 18var (
 19	ownerAddress = std.OriginCaller()
 20	oracle       *gnorkle.Instance
 21	postHandler  postGnorkleMessageHandler
 22
 23	handleToAddressMap = avl.NewTree()
 24	addressToHandleMap = avl.NewTree()
 25)
 26
 27func init() {
 28	oracle = gnorkle.NewInstance()
 29	oracle.AddToWhitelist("", []string{string(ownerAddress)})
 30}
 31
 32type 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.
 36func (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.
 72func RequestVerification(cur realm, githubHandle string) {
 73	gnoAddress := string(std.OriginCaller())
 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	std.Emit(
 87		"verification_requested",
 88		"from", gnoAddress,
 89		"handle", githubHandle,
 90	)
 91}
 92
 93// GnorkleEntrypoint is the entrypoint to the gnorkle oracle handler.
 94func GnorkleEntrypoint(cur realm, message string) string {
 95	result, err := oracle.HandleMessage(message, postHandler)
 96	if err != nil {
 97		panic(err)
 98	}
 99
100	return result
101}
102
103// SetOwner transfers ownership of the contract to the given address.
104func SetOwner(owner std.Address) {
105	if ownerAddress != std.OriginCaller() {
106		panic("only the owner can set a new owner")
107	}
108
109	ownerAddress = owner
110
111	// In the context of this contract, the owner is the only one that can
112	// add new feeds to the oracle.
113	oracle.ClearWhitelist("")
114	oracle.AddToWhitelist("", []string{string(ownerAddress)})
115}
116
117// GetHandleByAddress returns the github handle associated with the given gno address.
118func GetHandleByAddress(cur realm, address_XXX string) string {
119	if value, ok := addressToHandleMap.Get(address_XXX); ok {
120		return value.(string)
121	}
122
123	return ""
124}
125
126// GetAddressByHandle returns the gno address associated with the given github handle.
127func GetAddressByHandle(cur realm, handle string) string {
128	if value, ok := handleToAddressMap.Get(handle); ok {
129		return value.(string)
130	}
131
132	return ""
133}
134
135// Render returns a json object string will all verified handle -> address mappings.
136func Render(_ string) string {
137	result := "{"
138	var appendComma bool
139	handleToAddressMap.Iterate("", "", func(handle string, address_XXX any) bool {
140		if appendComma {
141			result += ","
142		}
143
144		result += `"` + handle + `": "` + address_XXX.(string) + `"`
145		appendComma = true
146
147		return false
148	})
149
150	return result + "}"
151}