Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/points for early test #45

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/iden3/go-rapidsnark/types v0.0.3
github.com/rarimo/decentralized-auth-svc v0.0.0-20240522134350-2694eafa9509
github.com/rarimo/saver-grpc-lib v1.0.0
github.com/rarimo/zkverifier-kit v1.0.0
github.com/rarimo/zkverifier-kit v1.2.4
github.com/rubenv/sql-migrate v1.6.1
github.com/stretchr/testify v1.9.0
gitlab.com/distributed_lab/ape v1.7.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2124,6 +2124,8 @@ github.com/rarimo/saver-grpc-lib v1.0.0 h1:MGUVjYg7unmodYczVsLqlqZNkT4CIgKqdo6aQ
github.com/rarimo/saver-grpc-lib v1.0.0/go.mod h1:DpugWK5B7Hi0bdC3MPe/9FD2zCxaRwsyykdwxtF1Zgg=
github.com/rarimo/zkverifier-kit v1.0.0 h1:zMW85hyDP3Uk6p9Dk9U4TBzOf0Pry+RNlWpli1tUZ1Q=
github.com/rarimo/zkverifier-kit v1.0.0/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/rarimo/zkverifier-kit v1.2.4 h1:AJ5ZAyOYOGR2QiDlOA2ul/QMZnjBZ/VzPqLjSIUbZgw=
github.com/rarimo/zkverifier-kit v1.2.4/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg=
Expand Down
89 changes: 89 additions & 0 deletions internal/cli/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cli

import (
"fmt"

"github.com/rarimo/rarime-points-svc/internal/config"
"github.com/rarimo/rarime-points-svc/internal/data"
"github.com/rarimo/rarime-points-svc/internal/data/evtypes"
"github.com/rarimo/rarime-points-svc/internal/data/pg"
"github.com/rarimo/rarime-points-svc/internal/service/handlers"
)

func emitEvent(cfg config.Config, timestamp int) {
log := cfg.Log()
db := cfg.DB()
lvls := cfg.Levels()
evTypes := cfg.EventTypes()

balancesQ := pg.NewBalances(db)
eventsQ := pg.NewEvents(db)
referralsQ := pg.NewReferrals(db)
countriesQ := pg.NewCountries(db)

evType := evTypes.Get(evtypes.TypeEarlyTest, evtypes.FilterInactive)

if evType == nil {
log.Infof("Event type %s is inactive", evtypes.TypeEarlyTest)
return
}

balances, err := balancesQ.FilterByCreatedAtBefore(timestamp).FilterUnverified().Select()
if err != nil {
panic(fmt.Errorf("failed to select balances for early test reward: %w", err))
}
if len(balances) == 0 {
log.Infof("No balances found")
return
}

nullifiers := make([]string, 0, len(balances))
for _, balance := range balances {
nullifiers = append(nullifiers, balance.Nullifier)
}

emittedEvents, err := eventsQ.New().FilterByType(evtypes.TypeEarlyTest).FilterByNullifier(nullifiers...).Select()
if err != nil {
panic(fmt.Errorf("failed to select emitted events: %w", err))
}

eventsMap := make(map[string]struct{}, len(emittedEvents))
for _, event := range emittedEvents {
eventsMap[event.Nullifier] = struct{}{}
}

for _, balance := range balances {
err = eventsQ.New().Transaction(func() error {
if _, exists := eventsMap[balance.Nullifier]; exists {
log.Infof("Event %s is already done for user with nullifier %s ", evtypes.TypeEarlyTest, balance.Nullifier)
return nil
}

err = eventsQ.Insert(data.Event{
Nullifier: balance.Nullifier,
Type: evtypes.TypeEarlyTest,
Status: data.EventFulfilled,
})

if err != nil {
return fmt.Errorf("failed to insert %s event: %w", evtypes.TypeEarlyTest, err)
}

if !evType.AutoClaim {
return nil
}

_, err = eventsQ.FilterByNullifier(balance.Nullifier).Update(data.EventClaimed, nil, &evType.Reward)
if err != nil {
return fmt.Errorf("failed to update %s events for user=%s: %w", evtypes.TypeEarlyTest, balance.Nullifier, err)
}

err := handlers.DoClaimEventUpdates(lvls, referralsQ, balancesQ, countriesQ, balance, evType.Reward)
if err != nil {
return fmt.Errorf("failed to do lvlup and referrals updates: %w", err)
}

return nil
})
}
}
7 changes: 7 additions & 0 deletions internal/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func Run(args []string) bool {
migrateCmd = app.Command("migrate", "migrate command")
migrateUpCmd = migrateCmd.Command("up", "migrate db up")
migrateDownCmd = migrateCmd.Command("down", "migrate db down")

eventCmd = app.Command("events", "manage events")
eventEmitCmd = eventCmd.Command("emit", "emit event")

onboarderBefore = eventEmitCmd.Arg("before", "balance onboarded before this timestamp").Required().Int()
)

cmd, err := app.Parse(args[1:])
Expand All @@ -48,6 +53,8 @@ func Run(args []string) bool {
err = MigrateUp(cfg)
case migrateDownCmd.FullCommand():
err = MigrateDown(cfg)
case eventEmitCmd.FullCommand():
emitEvent(cfg, *onboarderBefore)
default:
log.Errorf("unknown command %s", cmd)
return false
Expand Down
6 changes: 3 additions & 3 deletions internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/rarimo/rarime-points-svc/internal/service/workers/sbtcheck"
"github.com/rarimo/saver-grpc-lib/broadcaster"
zk "github.com/rarimo/zkverifier-kit"
"github.com/rarimo/zkverifier-kit/identity"
"github.com/rarimo/zkverifier-kit/root"
"gitlab.com/distributed_lab/kit/comfig"
"gitlab.com/distributed_lab/kit/kv"
"gitlab.com/distributed_lab/kit/pgdb"
Expand All @@ -34,7 +34,7 @@ type config struct {
comfig.Listenerer
auth.Auther
broadcaster.Broadcasterer
identity.VerifierProvider
root.VerifierProvider
evtypes.EventTypeser
sbtcheck.SbtChecker
countrier.Countrier
Expand All @@ -53,7 +53,7 @@ func New(getter kv.Getter) Config {
Logger: comfig.NewLogger(getter, comfig.LoggerOpts{}),
Auther: auth.NewAuther(getter), //nolint:misspell
Broadcasterer: broadcaster.New(getter),
VerifierProvider: identity.NewVerifierProvider(getter),
VerifierProvider: root.NewVerifierProvider(getter, root.PoseidonSMT),
EventTypeser: evtypes.NewConfig(getter),
SbtChecker: sbtcheck.NewConfig(getter),
Countrier: countrier.NewConfig(getter),
Expand Down
4 changes: 2 additions & 2 deletions internal/config/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (c *config) Verifier() *zk.Verifier {
panic(fmt.Errorf("failed to figure out verifier: %w", err))
}

v, err := zk.NewPassportVerifier(nil,
v, err := zk.NewVerifier(nil,
zk.WithVerificationKeyFile(cfg.VerificationKeyPath),
zk.WithAgeAbove(cfg.AllowedAge),
zk.WithIdentityVerifier(c.ProvideVerifier()),
zk.WithProofType(zk.GlobalPassport),
zk.WithProofSelectorValue(proofSelectorValue),
zk.WithEventID(proofEventIDValue),
zk.WithIdentitiesCounter(maxIdentityCount),
Expand Down
2 changes: 2 additions & 0 deletions internal/data/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ type BalancesQ interface {
WithoutPassportEvent() ([]WithoutPassportEventBalance, error)
WithoutReferralEvent() ([]ReferredReferrer, error)

FilterByCreatedAtBefore(timestamp int) BalancesQ
FilterByNullifier(...string) BalancesQ
FilterDisabled() BalancesQ
FilterByAnonymousID(id string) BalancesQ
FilterUnverified() BalancesQ
}

type WithoutPassportEventBalance struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/data/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type EventsQ interface {
SelectAbsentTypes(allTypes ...string) ([]ReopenableEvent, error)

FilterByID(...string) EventsQ
FilterByNullifier(string) EventsQ
FilterByNullifier(...string) EventsQ
FilterByStatus(...EventStatus) EventsQ
FilterByType(...string) EventsQ
FilterByNotType(types ...string) EventsQ
Expand Down
1 change: 1 addition & 0 deletions internal/data/evtypes/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (
TypeBeReferred = "be_referred"
TypeReferralSpecific = "referral_specific"
TypePassportScan = "passport_scan"
TypeEarlyTest = "early_test"
)

const (
Expand Down
8 changes: 8 additions & 0 deletions internal/data/pg/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@ func (q *balances) FilterByAnonymousID(id string) data.BalancesQ {
return q.applyCondition(squirrel.Eq{"anonymous_id": id})
}

func (q *balances) FilterByCreatedAtBefore(timestamp int) data.BalancesQ {
return q.applyCondition(squirrel.LtOrEq{"created_at": timestamp})
}

func (q *balances) FilterUnverified() data.BalancesQ {
return q.applyCondition(squirrel.NotEq{"anonymous_id": nil})
}

func (q *balances) applyCondition(cond squirrel.Sqlizer) data.BalancesQ {
q.selector = q.selector.Where(cond)
q.updater = q.updater.Where(cond)
Expand Down
2 changes: 1 addition & 1 deletion internal/data/pg/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (q *events) FilterByID(ids ...string) data.EventsQ {
return q.applyCondition(squirrel.Eq{"id": ids})
}

func (q *events) FilterByNullifier(nullifier string) data.EventsQ {
func (q *events) FilterByNullifier(nullifier ...string) data.EventsQ {
return q.applyCondition(squirrel.Eq{"nullifier": nullifier})
}

Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/claim_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func newClaimEventResponse(
}

resp := resources.EventResponse{Data: eventModel}
inc := newBalanceModel(balance)
inc := NewBalanceModel(balance)
resp.Included.Add(&inc)

return resp
Expand Down
6 changes: 3 additions & 3 deletions internal/service/handlers/get_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ func GetBalance(w http.ResponseWriter, r *http.Request) {
ape.Render(w, newBalanceResponse(*balance, referrals))
}

// newBalanceModel forms a balance response without referral fields, which must
// NewBalanceModel forms a balance response without referral fields, which must
// only be accessed with authorization.
func newBalanceModel(balance data.Balance) resources.Balance {
func NewBalanceModel(balance data.Balance) resources.Balance {
return resources.Balance{
Key: resources.Key{
ID: balance.Nullifier,
Expand All @@ -76,7 +76,7 @@ func newBalanceModel(balance data.Balance) resources.Balance {
}

func newBalanceResponse(balance data.Balance, referrals []data.Referral) resources.BalanceResponse {
resp := resources.BalanceResponse{Data: newBalanceModel(balance)}
resp := resources.BalanceResponse{Data: NewBalanceModel(balance)}
boolP := func(b bool) *bool { return &b }

resp.Data.Attributes.IsDisabled = boolP(!balance.ReferredBy.Valid)
Expand Down
2 changes: 1 addition & 1 deletion internal/service/handlers/leaderboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Leaderboard(w http.ResponseWriter, r *http.Request) {
func newLeaderboardResponse(balances []data.Balance) resources.BalanceListResponse {
list := make([]resources.Balance, len(balances))
for i, balance := range balances {
list[i] = newBalanceModel(balance)
list[i] = NewBalanceModel(balance)
}

return resources.BalanceListResponse{Data: list}
Expand Down
4 changes: 2 additions & 2 deletions internal/service/handlers/list_withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ func ListWithdrawals(w http.ResponseWriter, r *http.Request) {
func newWithdrawalsResponse(withdrawals []data.Withdrawal) resources.WithdrawalListResponse {
list := make([]resources.Withdrawal, len(withdrawals))
for i, w := range withdrawals {
list[i] = newWithdrawalModel(w)
list[i] = NewWithdrawalModel(w)
}
return resources.WithdrawalListResponse{Data: list}
}

func newWithdrawalModel(w data.Withdrawal) resources.Withdrawal {
func NewWithdrawalModel(w data.Withdrawal) resources.Withdrawal {
return resources.Withdrawal{
Key: resources.Key{
ID: w.ID,
Expand Down
11 changes: 6 additions & 5 deletions internal/service/handlers/verify_passport.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"github.com/rarimo/rarime-points-svc/internal/service/requests"
"github.com/rarimo/rarime-points-svc/resources"
zk "github.com/rarimo/zkverifier-kit"
"github.com/rarimo/zkverifier-kit/identity"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)
Expand Down Expand Up @@ -207,11 +206,13 @@ func getAndVerifyBalanceEligibility(
proof.PubSignals[zk.Nullifier] = mustHexToInt(nullifier)
err = Verifier(r).VerifyProof(*proof)
if err != nil {
if errors.Is(err, identity.ErrContractCall) {
Log(r).WithError(err).Error("Failed to verify proof")
return nil, append(errs, problems.InternalError())
var vErr validation.Errors
if errors.As(err, &vErr) {
return nil, problems.BadRequest(err)
}
return nil, problems.BadRequest(err)

Log(r).WithError(err).Error("Failed to verify proof")
return nil, append(errs, problems.InternalError())
}

return balance, nil
Expand Down
14 changes: 11 additions & 3 deletions internal/service/handlers/withdraw.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package handlers

import (
"errors"
"fmt"
"net/http"

Expand Down Expand Up @@ -53,7 +54,14 @@ func Withdraw(w http.ResponseWriter, r *http.Request) {

err = Verifier(r).VerifyProof(proof, zk.WithEventData(addr))
if err != nil {
ape.RenderErr(w, problems.BadRequest(err)...)
var vErr validation.Errors
if errors.As(err, &vErr) {
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

Log(r).WithError(err).Error("Failed to verify proof")
ape.RenderErr(w, problems.InternalError())
return
}

Expand Down Expand Up @@ -127,7 +135,7 @@ func Withdraw(w http.ResponseWriter, r *http.Request) {
}

func newWithdrawResponse(w data.Withdrawal, balance data.Balance) *resources.WithdrawalResponse {
wm := newWithdrawalModel(w)
wm := NewWithdrawalModel(w)
wm.Relationships = &resources.WithdrawalRelationships{
Balance: resources.Relation{
Data: &resources.Key{
Expand All @@ -138,7 +146,7 @@ func newWithdrawResponse(w data.Withdrawal, balance data.Balance) *resources.Wit
}

resp := resources.WithdrawalResponse{Data: wm}
bm := newBalanceModel(balance)
bm := NewBalanceModel(balance)
resp.Included.Add(&bm)

return &resp
Expand Down
11 changes: 7 additions & 4 deletions internal/service/requests/verify_passport.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,26 @@ func NewVerifyPassport(r *http.Request) (req resources.VerifyPassportRequest, er
val.When(verifyPassportPathRegexp.MatchString(r.URL.Path), val.Required),
val.When(joinProgramPathRegexp.MatchString(r.URL.Path), val.Nil)),
"data/attributes/proof/proof": val.Validate(proof.Proof, val.When(attr.Proof != nil, val.Required)),
"data/attributes/proof/pub_signals": val.Validate(proof.PubSignals, val.When(attr.Proof != nil, val.Required, val.Length(22, 22))),
"data/attributes/proof/pub_signals": val.Validate(proof.PubSignals, val.When(attr.Proof != nil, val.Required, val.Length(23, 23))),
}.Filter()
}

// ExtractCountry extracts country code from the proof, converting decimal UTF-8
// code to ISO 3166-1 alpha-3 code.
func ExtractCountry(proof zkptypes.ZKProof) (string, error) {
if len(proof.PubSignals) <= int(zk.Citizenship) {
if len(proof.PubSignals) <= zk.Indexes(zk.GlobalPassport)[zk.Citizenship] {
return "", val.Errors{"country_code": val.ErrLengthTooShort}.Filter()
}

b, ok := new(big.Int).SetString(proof.PubSignals[zk.Citizenship], 10)
getter := zk.PubSignalGetter{Signals: proof.PubSignals, ProofType: zk.GlobalPassport}
code := getter.Get(zk.Citizenship)

b, ok := new(big.Int).SetString(code, 10)
if !ok {
b = new(big.Int)
}

code := string(b.Bytes())
code = string(b.Bytes())

return code, val.Errors{"country_code": val.Validate(code, val.Required, is.CountryCode3)}.Filter()
}
Loading
Loading