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  }