Skip to content

Commit 624b1fc

Browse files
committed
feat: github oracle
1 parent 4470e0e commit 624b1fc

File tree

9 files changed

+371
-0
lines changed

9 files changed

+371
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
gnokey maketx addpkg \
2+
-deposit="1ugnot" \
3+
-gas-fee="1ugnot" \
4+
-gas-wanted="5000000" \
5+
-broadcast="true" \
6+
-remote="https://rpc.gno.land:443" \
7+
-chainid="portal-loop" \
8+
-pkgdir="." \
9+
-pkgpath="gno.land/r/mikecito/gh_test_4" \
10+
mykey2
11+
12+
gnokey maketx call \
13+
-gas-fee="1ugnot" \
14+
-gas-wanted="5000000" \
15+
-broadcast="true" \
16+
-remote="https://rpc.gno.land:443" \
17+
-chainid="portal-loop" \
18+
-pkgpath="gno.land/r/mikecito/gh_test_4" \
19+
-func="AdminSetOracleAddr" \
20+
-args="g1d46t4el0dduffs5j56t2razaeyvnmkxlduduuw" \
21+
mykey2
22+
23+
gnokey maketx call \
24+
-gas-fee="1ugnot" \
25+
-gas-wanted="5000000" \
26+
-broadcast="true" \
27+
-remote="https://rpc.gno.land:443" \
28+
-chainid="portal-loop" \
29+
-pkgpath="gno.land/r/mikecito/gh_test_4" \
30+
-func="OracleUpsertAccount" \
31+
-args="15034695" \
32+
-args="omarsy" \
33+
-args="6h057" \
34+
-args="user" \
35+
mykey2
36+
37+
gnokey query "vm/qeval" -data='gno.land/r/mikecito/gh_test_4
38+
AccountByID("15034695")' -remote="https://rpc.gno.land:443"
39+
40+
gnokey query "vm/qeval" -data='gno.land/r/mikecito/gh_test_4
41+
RenderAccount("g14mfv59k38r8k5vkevpu0lpqlqra0e9trwp3d32")' -remote="https://rpc.gno.land:443"
42+
43+
gnokey query "vm/qeval" -data='gno.land/r/mikecito/gh_test_4
44+
Render()' -remote="https://rpc.gno.land:443"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package gh
2+
3+
import (
4+
"errors"
5+
"std"
6+
7+
"gno.land/p/demo/avl"
8+
)
9+
10+
// Account represents a GitHub user account or organization.
11+
type Account struct {
12+
id string
13+
login string
14+
name string
15+
kind string
16+
}
17+
18+
func (a Account) ID() string { return a.id }
19+
func (a Account) Name() string { return a.name }
20+
func (a Account) Kind() string { return a.kind }
21+
func (a Account) URL() string { return "https://github.com/" + a.login }
22+
func (a Account) IsUser() bool { return a.kind == UserAccount }
23+
func (a Account) IsOrg() bool { return a.kind == OrgAccount }
24+
25+
// TODO: func (a Account) RepoByID() Repo ...
26+
27+
func (a Account) Validate() error {
28+
if a.id == "" {
29+
return errors.New("empty id")
30+
}
31+
32+
if a.login == "" {
33+
return errors.New("empty login")
34+
}
35+
if a.kind == "" || (a.kind != UserAccount && a.kind != OrgAccount) {
36+
return errors.New("empty kind")
37+
}
38+
if a.name == "" {
39+
return errors.New("empty name")
40+
}
41+
// TODO: validate
42+
return nil
43+
}
44+
45+
func (a Account) Render() string {
46+
return `{ "id": "` + a.id + `", "login": "` + a.login + `", "name": "` + a.name + `", "kind": "` + a.kind + `" }`
47+
}
48+
49+
func (a Account) String() string {
50+
// XXX: better idea?
51+
return a.URL()
52+
}
53+
54+
const (
55+
UserAccount string = "user"
56+
OrgAccount string = "org"
57+
)
58+
59+
func AccountByID(id string) *Account {
60+
res, ok := accounts.Get(id)
61+
if !ok {
62+
return nil
63+
}
64+
65+
return res.(*Account)
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
)
6+
7+
var(
8+
adminAddr std.Address = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq"
9+
)
10+
11+
12+
func assertIsAdmin() {
13+
if std.GetOrigCaller() != adminAddr {
14+
panic("restricted area")
15+
}
16+
}
17+
18+
func setAdminAddress(address std.Address) {
19+
adminAddr = address
20+
}
21+
22+
func SetAdminAddress(address std.Address) {
23+
assertIsAdmin()
24+
setAdminAddress(address)
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
"testing"
6+
7+
"gno.land/p/demo/avl"
8+
"gno.land/p/demo/testutils"
9+
)
10+
11+
func TestAssertIsAdmin(t *testing.T) {
12+
adminAddr = "g1test1234"
13+
randomuser := testutils.TestAddress("g1unknown_player")
14+
defer func() {
15+
if r := recover(); r != nil {
16+
}
17+
}()
18+
std.TestSetOrigCaller(randomuser)
19+
assertIsAdmin()
20+
t.Fatalf("should fail because not admin")
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module gno.land/r/demo/teritori/gh
2+
3+
require (
4+
gno.land/p/demo/avl v0.0.0-latest
5+
)
6+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
"strings"
6+
"time"
7+
8+
"gno.land/p/demo/avl"
9+
)
10+
11+
var (
12+
accounts avl.Tree // id -> Account
13+
lastUpdateTime time.Time // used by the bot to only upload the diff
14+
oracleAddr std.Address
15+
)
16+
17+
func OracleLastUpdated() time.Time { return lastUpdateTime }
18+
19+
func OracleUpsertAccount(id, login, name, kind string) {
20+
assertIsOracle()
21+
lastUpdateTime = time.Now()
22+
23+
// get or create
24+
account := &Account{}
25+
res, ok := accounts.Get(id)
26+
if ok {
27+
account = res.(*Account)
28+
} else {
29+
account.id = id
30+
}
31+
32+
// update fields
33+
account.name = name
34+
account.kind = kind
35+
account.login = login
36+
37+
if err := account.Validate(); err != nil {
38+
panic(err)
39+
}
40+
41+
// save
42+
accounts.Set(id, account)
43+
}
44+
45+
func AdminSetOracleAddr(new std.Address) {
46+
assertIsAdmin()
47+
oracleAddr = new
48+
}
49+
50+
func assertIsOracle() {
51+
if std.GetOrigCaller() != oracleAddr {
52+
panic("restricted area")
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
"testing"
6+
"time"
7+
8+
"gno.land/p/demo/avl"
9+
"gno.land/p/demo/testutils"
10+
)
11+
12+
func TestOracleUpsertUserIsNotOracle(t *testing.T) {
13+
oracleAddr = "g1test1234"
14+
var randomuser = "g1unknown_player"
15+
user := testutils.TestAddress("user")
16+
defer func() {
17+
if r := recover(); r != nil {
18+
}
19+
}()
20+
OracleUpsertAccount("acountID", "villaquiranm","john doe","user")
21+
t.Fatalf("should fail because not admin")
22+
}
23+
24+
func TestOracleUpsertUserOk(t *testing.T) {
25+
oracleAddr = "g1random"
26+
if accounts.Size() != 0 {
27+
t.Fatalf("Accounts is not empty")
28+
}
29+
now := time.Now()
30+
31+
std.TestSetOrigCaller(oracleAddr)
32+
33+
var randomuser = "g1unknown_player"
34+
OracleUpsertAccount("acountID", "villaquiranm","john doe","user")
35+
36+
if accounts.Size() != 1 {
37+
t.Fatalf("User was not created")
38+
}
39+
40+
OracleUpsertAccount("acountID", "villaquiranm","john doe","user")
41+
42+
if accounts.Size() != 1 {
43+
t.Fatalf("User was created more than once")
44+
}
45+
46+
if OracleLastUpdated().Unix() < now.Unix() {
47+
t.Fatalf("OracleLastUpdated was not changed")
48+
}
49+
}
50+
51+
func TestAssertIsOracle(t *testing.T) {
52+
std.TestSetOrigCaller(adminAddr)
53+
AdminSetOracleAddr("g1random123")
54+
defer func() {
55+
if r := recover(); r != nil {
56+
}
57+
}()
58+
assertIsOracle()
59+
t.Fatalf("should fail because user is not oracle")
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
6+
"gno.land/p/demo/avl"
7+
)
8+
9+
var (
10+
addressToAccount avl.Tree // address -> Account
11+
guardian std.Address // guardian address
12+
)
13+
14+
func init() {
15+
addressToAccount = avl.Tree{}
16+
setAdminAddress(std.GetOrigCaller())
17+
}
18+
19+
// Todo maybe we should gave multi guardian
20+
func SetGuardian(address std.Address) {
21+
assertIsAdmin()
22+
guardian = address
23+
}
24+
25+
func LinkAccount(accountID string, address std.Address, signature string) {
26+
verifySignature(accountID+" "+address.String(), signature)
27+
28+
account := AccountByID(accountID)
29+
if account == nil {
30+
panic("account not found")
31+
}
32+
33+
addressToAccount.Set(address.String(), account)
34+
}
35+
36+
func RenderAccount(address std.Address) string {
37+
account, ok := addressToAccount.Get(address.String())
38+
if !ok {
39+
panic("account not found")
40+
}
41+
42+
return account.(*Account).Render()
43+
}
44+
45+
func Render(address string) string {
46+
if address != "" {
47+
return RenderAccount(std.Address(address))
48+
}
49+
str := "["
50+
addressToAccount.Iterate("", "", func(key string, value interface{}) bool {
51+
account := value.(*Account)
52+
str += account.Render()
53+
str += ","
54+
return false
55+
})
56+
str += "]"
57+
58+
return str
59+
}
60+
61+
func verifySignature(message, signature string) bool {
62+
return true
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package gh
2+
3+
import (
4+
"std"
5+
"testing"
6+
7+
"gno.land/p/demo/avl"
8+
"gno.land/p/demo/testutils"
9+
)
10+
11+
func TestLinkAccount(t *testing.T) {
12+
adminAddr = "g1test1234"
13+
14+
user := testutils.TestAddress("user")
15+
admin := testutils.TestAddress("admin")
16+
17+
std.TestSetOrigCaller(admin)
18+
setAdminAddress(admin)
19+
AdminSetOracleAddr(admin)
20+
21+
OracleUpsertAccount("123","user", "user", UserAccount)
22+
LinkAccount("123", user, "signature")
23+
accountInPublic, ok := addressToAccount.Get(user.String())
24+
if !ok {
25+
t.Fatalf("account not found")
26+
}
27+
account := AccountByID("123")
28+
if accountInPublic != account {
29+
t.Fatalf("account is not same")
30+
}
31+
}

0 commit comments

Comments
 (0)