diff --git a/cmd/main.go b/cmd/main.go index b8b31d3..af3237c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,6 +12,8 @@ import ( "git.grassecon.net/grassrootseconomics/visedriver/storage" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/config" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/event/nats" + "git.grassecon.net/grassrootseconomics/sarafu-vise-events/lookup" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) var ( @@ -41,7 +43,8 @@ func main() { menuStorageService := storage.NewMenuStorageService(connData, "") - n := nats.NewNatsSubscription(menuStorageService) + eh := viseevent.NewEventsHandler(lookup.Api) + n := nats.NewNatsSubscription(menuStorageService, eh) err = n.Connect(ctx, config.JetstreamURL) if err != nil { fmt.Fprintf(os.Stderr, "Stream connect err: %v", err) diff --git a/event/custodial_registration.go b/event/custodial_registration.go index df79495..85a4b8c 100644 --- a/event/custodial_registration.go +++ b/event/custodial_registration.go @@ -1,29 +1,19 @@ package event import ( - "context" - geEvent "github.com/grassrootseconomics/eth-tracker/pkg/event" - - "git.defalsify.org/vise.git/persist" - "git.grassecon.net/grassrootseconomics/sarafu-vise/store" - "git.grassecon.net/grassrootseconomics/sarafu-vise-events/lookup" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" ) const ( - evReg = "CUSTODIAL_REGISTRATION" - accountCreatedFlag = 9 + evReg = apievent.EventRegistrationTag + //accountCreatedFlag = 9 ) -// fields used for handling custodial registration event. -type eventCustodialRegistration struct { - Account string -} - // attempt to coerce event as custodial registration. -func asCustodialRegistrationEvent(gev *geEvent.Event) (*eventCustodialRegistration, bool) { +func asCustodialRegistrationEvent(gev *geEvent.Event) (*apievent.EventCustodialRegistration, bool) { var ok bool - var ev eventCustodialRegistration + var ev apievent.EventCustodialRegistration if gev.TxType != evReg { return nil, false } @@ -35,22 +25,3 @@ func asCustodialRegistrationEvent(gev *geEvent.Event) (*eventCustodialRegistrati logg.Debugf("parsed ev", "pl", pl, "ev", ev) return &ev, true } - -// handle custodial registration. -// -// TODO: implement account created in userstore instead, so that -// the need for persister and state use here is eliminated (it -// introduces concurrency risks) -func handleCustodialRegistration(ctx context.Context, userStore *store.UserDataStore, pr *persist.Persister, ev *eventCustodialRegistration) error { - identity, err := lookup.IdentityFromAddress(ctx, userStore, ev.Account) - if err != nil { - return err - } - err = pr.Load(identity.SessionId) - if err != nil { - return err - } - st := pr.GetState() - st.SetFlag(accountCreatedFlag) - return pr.Save(identity.SessionId) -} diff --git a/event/custodial_test.go b/event/custodial_test.go index 8d92b6a..c99f93c 100644 --- a/event/custodial_test.go +++ b/event/custodial_test.go @@ -9,11 +9,14 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/state" "git.defalsify.org/vise.git/cache" + "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/config" "git.grassecon.net/grassrootseconomics/common/hex" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" "git.grassecon.net/grassrootseconomics/sarafu-vise/store" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/internal/testutil" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) func TestCustodialRegistration(t *testing.T) { @@ -53,10 +56,13 @@ func TestCustodialRegistration(t *testing.T) { t.Fatal(err) } - ev := &eventCustodialRegistration{ + ev := &apievent.EventCustodialRegistration{ Account: testutil.AliceChecksum, } - err = handleCustodialRegistration(ctx, &store, pr, ev) + + // Use dev service or mock service instead + eh := viseevent.NewEventsHandler(&http.HTTPAccountService{}) + err = eh.HandleCustodialRegistration(ctx, &store, pr, ev) if err != nil { t.Fatal(err) } diff --git a/event/nats/nats.go b/event/nats/nats.go index 1b48176..a581c33 100644 --- a/event/nats/nats.go +++ b/event/nats/nats.go @@ -12,6 +12,7 @@ import ( "git.grassecon.net/grassrootseconomics/visedriver/storage" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/event" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/config" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) var ( @@ -22,7 +23,7 @@ var ( // // Extends Router. type NatsSubscription struct { - event.Router + *event.Router ctx context.Context conn *nats.Conn js jetstream.JetStream @@ -31,11 +32,9 @@ type NatsSubscription struct { } // NewNatsSubscription creates a new NatsSubscription with the given user store. -func NewNatsSubscription(store storage.StorageService) *NatsSubscription { +func NewNatsSubscription(store storage.StorageService, handler *viseevent.EventsHandler) *NatsSubscription { return &NatsSubscription{ - Router: event.Router{ - Store: store, - }, + Router: event.NewRouter(store, handler), } } diff --git a/event/nats/nats_test.go b/event/nats/nats_test.go index 9ed4d93..7d338ef 100644 --- a/event/nats/nats_test.go +++ b/event/nats/nats_test.go @@ -22,6 +22,7 @@ import ( "git.grassecon.net/grassrootseconomics/common/hex" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/internal/testutil" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) const ( @@ -125,6 +126,7 @@ func TestHandleMsg(t *testing.T) { }, } lookup.Api = api + eh := viseevent.NewEventsHandler(api) ctx := context.Background() userDb := memdb.NewMemDb() @@ -148,7 +150,8 @@ func TestHandleMsg(t *testing.T) { storageService := &testutil.TestStorageService{ Store: userDb, } - sub := NewNatsSubscription(storageService) + + sub := NewNatsSubscription(storageService, eh) data := fmt.Sprintf(`{ "block": %d, diff --git a/event/route.go b/event/route.go index 7d8c8a0..a9895f6 100644 --- a/event/route.go +++ b/event/route.go @@ -9,6 +9,7 @@ import ( "git.defalsify.org/vise.git/logging" "git.grassecon.net/grassrootseconomics/visedriver/storage" "git.grassecon.net/grassrootseconomics/sarafu-vise/store" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) var ( @@ -17,7 +18,15 @@ var ( // Router is responsible for invoking handlers corresponding to events. type Router struct { - Store storage.StorageService + store storage.StorageService + handler *viseevent.EventsHandler +} + +func NewRouter(store storage.StorageService, handler *viseevent.EventsHandler) *Router { + return &Router{ + store: store, + handler: handler, + } } // Route parses an event from the event stream, and resolves the handler @@ -27,7 +36,7 @@ type Router struct { // handler fails to successfully execute. func(r *Router) Route(ctx context.Context, gev *geEvent.Event) error { logg.DebugCtxf(ctx, "have event", "ev", gev) - userDb, err := r.Store.GetUserdataDb(ctx) + userDb, err := r.store.GetUserdataDb(ctx) if err != nil { return err } @@ -36,15 +45,15 @@ func(r *Router) Route(ctx context.Context, gev *geEvent.Event) error { } evCC, ok := asCustodialRegistrationEvent(gev) if ok { - pr, err := r.Store.GetPersister(ctx) + pr, err := r.store.GetPersister(ctx) if err != nil { return err } - return handleCustodialRegistration(ctx, userStore, pr, evCC) + return r.handler.HandleCustodialRegistration(ctx, userStore, pr, evCC) } evTT, ok := asTokenTransferEvent(gev) if ok { - return handleTokenTransfer(ctx, userStore, evTT) + return r.handler.HandleTokenTransfer(ctx, userStore, evTT) } return fmt.Errorf("unexpected message") diff --git a/event/token.go b/event/token.go index 6ee8da2..1f91ab2 100644 --- a/event/token.go +++ b/event/token.go @@ -3,172 +3,48 @@ package event import ( "context" "fmt" - "strings" "strconv" geEvent "github.com/grassrootseconomics/eth-tracker/pkg/event" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - "git.defalsify.org/vise.git/db" - "git.grassecon.net/grassrootseconomics/sarafu-vise/store" "git.grassecon.net/grassrootseconomics/common/hex" - storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" - "git.grassecon.net/grassrootseconomics/sarafu-vise-events/lookup" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" ) const ( - evTokenTransfer = "TOKEN_TRANSFER" + evTokenTransfer = apievent.EventTokenTransferTag // TODO: export from visedriver storage package - DATATYPE_USERSUB = 64 + //DATATYPE_USERSUB = 64 ) -// fields used for handling token transfer event. -type eventTokenTransfer struct { - To string - Value int - VoucherAddress string - TxHash string - From string -} - -type eventTokenMint struct { - To string - Value int - TxHash string - VoucherAddress string -} - - // formatter for transaction data // // TODO: current formatting is a placeholder. -func formatTransaction(idx int, tx dataserviceapi.Last10TxResponse) string { - return fmt.Sprintf("%d %s %s", idx, tx.DateBlock, tx.TxHash[:10]) +func formatTransaction(tag string, idx int, item any) string { + if tag == apievent.EventTokenTransferTag { + tx, ok := item.(dataserviceapi.Last10TxResponse) + if !ok { + logg.Errorf("invalid formatting object", "tag", tag) + return "" + } + return fmt.Sprintf("%d %s %s", idx, tx.DateBlock, tx.TxHash[:10]) + } + logg.Warnf("unhandled formatting object", "tag", tag) + return "" } -// refresh and store transaction history. -func updateTokenTransferList(ctx context.Context, userStore *store.UserDataStore, identity lookup.Identity) error { - var r []string - - txs, err := lookup.Api.FetchTransactions(ctx, identity.ChecksumAddress) - if err != nil { - return err - } - - for i, tx := range(txs) { - r = append(r, formatTransaction(i, tx)) - } - - s := strings.Join(r, "\n") - - return userStore.WriteEntry(ctx, identity.SessionId, storedb.DATA_TRANSACTIONS, []byte(s)) -} - -// refresh and store token list. -func updateTokenList(ctx context.Context, userStore *store.UserDataStore, identity lookup.Identity) error { - holdings, err := lookup.Api.FetchVouchers(ctx, identity.ChecksumAddress) - if err != nil { - return err - } - metadata := store.ProcessVouchers(holdings) - _ = metadata - - // TODO: make sure subprefixdb is thread safe when using gdbm - // TODO: why is address session here unless explicitly set - pfxDb := toPrefixDb(userStore, identity.SessionId) - - typ := storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS) - err = pfxDb.Put(ctx, typ, []byte(metadata.Symbols)) - if err != nil { - return err - } - - typ = storedb.ToBytes(storedb.DATA_VOUCHER_BALANCES) - err = pfxDb.Put(ctx, typ, []byte(metadata.Balances)) - if err != nil { - return err - } - - typ = storedb.ToBytes(storedb.DATA_VOUCHER_DECIMALS) - err = pfxDb.Put(ctx, typ, []byte(metadata.Decimals)) - if err != nil { - return err - } - - typ = storedb.ToBytes(storedb.DATA_VOUCHER_ADDRESSES) - err = pfxDb.Put(ctx, typ, []byte(metadata.Addresses)) - if err != nil { - return err - } - - return nil -} - -// set default token to given symbol. -func updateDefaultToken(ctx context.Context, userStore *store.UserDataStore, identity lookup.Identity, activeSym string) error { - pfxDb := toPrefixDb(userStore, identity.SessionId) - // TODO: the activeSym input should instead be newline separated list? - tokenData, err := store.GetVoucherData(ctx, pfxDb, activeSym) - if err != nil { - return err - } - return store.UpdateVoucherData(ctx, userStore, identity.SessionId, tokenData) -} // waiter to check whether object is available on dependency endpoints. func updateWait(ctx context.Context) error { return nil } -// use api to resolve address to token symbol. -func toSym(ctx context.Context, address string) ([]byte, error) { - voucherData, err := lookup.Api.VoucherData(ctx, address) - if err != nil { - return nil, err - } - return []byte(voucherData.TokenSymbol), nil -} - -// execute all -func updateToken(ctx context.Context, userStore *store.UserDataStore, identity lookup.Identity, tokenAddress string) error { - err := updateTokenList(ctx, userStore, identity) - if err != nil { - return err - } - - userStore.Db.SetSession(identity.SessionId) - activeSym, err := userStore.ReadEntry(ctx, identity.SessionId, storedb.DATA_ACTIVE_SYM) - if err == nil { - return nil - } - if !db.IsNotFound(err) { - return err - } - if activeSym == nil { - activeSym, err = toSym(ctx, tokenAddress) - if err != nil { - return err - } - } - - err = updateDefaultToken(ctx, userStore, identity, string(activeSym)) - if err != nil { - return err - } - - err = updateTokenTransferList(ctx, userStore, identity) - if err != nil { - return err - } - - return nil -} - // attempt to coerce event as token transfer event. -func asTokenTransferEvent(gev *geEvent.Event) (*eventTokenTransfer, bool) { +func asTokenTransferEvent(gev *geEvent.Event) (*apievent.EventTokenTransfer, bool) { var err error var ok bool - var ev eventTokenTransfer + var ev apievent.EventTokenTransfer if gev.TxType != evTokenTransfer { return nil, false @@ -204,58 +80,3 @@ func asTokenTransferEvent(gev *geEvent.Event) (*eventTokenTransfer, bool) { return &ev, true } - -// handle token transfer. -// -// if from and to are NOT the same, handle code will be executed once for each side of the transfer. -func handleTokenTransfer(ctx context.Context, userStore *store.UserDataStore, ev *eventTokenTransfer) error { - identity, err := lookup.IdentityFromAddress(ctx, userStore, ev.From) - if err != nil { - if !db.IsNotFound(err) { - return err - } - } else { - err = updateToken(ctx, userStore, identity, ev.VoucherAddress) - if err != nil { - return err - } - } - - if strings.Compare(ev.To, ev.From) != 0 { - identity, err = lookup.IdentityFromAddress(ctx, userStore, ev.To) - if err != nil { - if !db.IsNotFound(err) { - return err - } - } else { - err = updateToken(ctx, userStore, identity, ev.VoucherAddress) - if err != nil { - return err - } - } - } - - return nil -} - -// handle token mint. -func handleTokenMint(ctx context.Context, userStore *store.UserDataStore, ev *eventTokenMint) error { - identity, err := lookup.IdentityFromAddress(ctx, userStore, ev.To) - if err != nil { - if !db.IsNotFound(err) { - return err - } - } else { - err = updateToken(ctx, userStore, identity, ev.VoucherAddress) - if err != nil { - return err - } - } - return nil -} - -func toPrefixDb(userStore *store.UserDataStore, sessionId string) storedb.PrefixDb { - userStore.Db.SetSession(sessionId) - prefix := storedb.ToBytes(db.DATATYPE_USERDATA) - return store.StoreToPrefixDb(userStore, prefix) -} diff --git a/event/token_test.go b/event/token_test.go index 8b1563c..43142c6 100644 --- a/event/token_test.go +++ b/event/token_test.go @@ -20,6 +20,8 @@ import ( "git.grassecon.net/grassrootseconomics/sarafu-vise-events/lookup" "git.grassecon.net/grassrootseconomics/common/hex" "git.grassecon.net/grassrootseconomics/sarafu-vise-events/internal/testutil" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" + viseevent "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" ) const ( @@ -72,6 +74,7 @@ func TestTokenTransfer(t *testing.T) { }, } lookup.Api = api + eh := viseevent.NewEventsHandler(api) ctx := context.Background() userDb := memdb.NewMemDb() @@ -96,12 +99,13 @@ func TestTokenTransfer(t *testing.T) { Db: userDb, } - ev := &eventTokenTransfer{ + ev := &apievent.EventTokenTransfer{ From: testutil.BobChecksum, To: testutil.AliceChecksum, Value: txValue, } - err = handleTokenTransfer(ctx, &userStore, ev) + + err = eh.HandleTokenTransfer(ctx, &userStore, ev) if err != nil { t.Fatal(err) } @@ -183,6 +187,7 @@ func TestTokenMint(t *testing.T) { }, } lookup.Api = api + eh := viseevent.NewEventsHandler(api) ctx := context.Background() userDb := memdb.NewMemDb() @@ -206,11 +211,11 @@ func TestTokenMint(t *testing.T) { Db: userDb, } - ev := &eventTokenMint{ + ev := &apievent.EventTokenMint{ To: testutil.AliceChecksum, Value: txValue, } - err = handleTokenMint(ctx, &userStore, ev) + err = eh.HandleTokenMint(ctx, &userStore, ev) if err != nil { t.Fatal(err) } diff --git a/go.mod b/go.mod index e6fbbfc..2610c8f 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.23.4 require ( git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d git.grassecon.net/grassrootseconomics/common v0.0.0-20250112155828-b55686e830fd - git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113085238-f65abf625d34 - git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113103240-5e02d62a1296 + git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113163355-25149ff2658c + git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113172627-fee439121dc9 git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113103142-5bf0a0e85893 github.com/grassrootseconomics/eth-tracker v1.3.0-rc github.com/grassrootseconomics/ussd-data-service v1.2.0-beta diff --git a/go.sum b/go.sum index aea1052..0298fa7 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,10 @@ git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d h1:bPAOVZOX4frSG git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.grassecon.net/grassrootseconomics/common v0.0.0-20250112155828-b55686e830fd h1:OG20aG/sC5yNaU2/rzoAKYp2f0u4lmGP5/Qax8UQD5c= git.grassecon.net/grassrootseconomics/common v0.0.0-20250112155828-b55686e830fd/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= -git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113085238-f65abf625d34 h1:Pl4OFKTeSehSTMMRUFosZUIDyUZRdXqm6CFn0+5+4qs= -git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113085238-f65abf625d34/go.mod h1:PEjJnAH5gYcjb4RRevaPCswa5VGGNqHSuyapZvIVR6w= -git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113103240-5e02d62a1296 h1:AvmADSvQ3UprqcT5q+3YRaSWb7DuIA2sl4SmWfCz8B4= -git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113103240-5e02d62a1296/go.mod h1:jqzGG4ehnh0/KrgCDbOhyyZgGILHiSp1vSf4J6cB9cE= +git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113163355-25149ff2658c h1:XiZQL9XhMl7bIxHWCqaQxE+haK/IKVlf53Q1UXVZqS4= +git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113163355-25149ff2658c/go.mod h1:e9Damfk0euyjWzn+CE8uxgg8p4ggd07wp99SOyEhGXY= +git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113172627-fee439121dc9 h1:HbKRb7ZyWJL8HG9RN1CZtgtdL6Rqy5ikYd/H9ZU/+XI= +git.grassecon.net/grassrootseconomics/sarafu-vise v0.0.0-20250113172627-fee439121dc9/go.mod h1:jqzGG4ehnh0/KrgCDbOhyyZgGILHiSp1vSf4J6cB9cE= git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113103142-5bf0a0e85893 h1:MyDINzwY1sjfXkIFoc+6T5lXF/1xdFV6yjHrpSNZzWM= git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113103142-5bf0a0e85893/go.mod h1:E6W7ZOa7ZvVr0Bc5ot0LNSwpSPYq4hXlAIvEPy3AJ7U= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= diff --git a/lookup/db.go b/lookup/db.go deleted file mode 100644 index 087440a..0000000 --- a/lookup/db.go +++ /dev/null @@ -1,57 +0,0 @@ -package lookup - -import ( - "context" - - "git.defalsify.org/vise.git/db" - "git.defalsify.org/vise.git/logging" - "git.grassecon.net/grassrootseconomics/sarafu-vise/store" - storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" - "git.grassecon.net/grassrootseconomics/common/hex" -) - -var ( - logg = logging.NewVanilla().WithDomain("term-lookup") -) - -// Identity contains all flavors of identifiers used across stream, api and -// client for a single agent. -type Identity struct { - NormalAddress string - ChecksumAddress string - SessionId string -} - -// IdentityFromAddress fully populates and Identity object from a given -// checksum address. -// -// It is the caller's responsibility to ensure that a valid checksum address -// is passed. -func IdentityFromAddress(ctx context.Context, userStore *store.UserDataStore, address string) (Identity, error) { - var err error - var ident Identity - - ident.ChecksumAddress = address - ident.NormalAddress, err = hex.NormalizeHex(ident.ChecksumAddress) - if err != nil { - return ident, err - } - ident.SessionId, err = getSessionIdByAddress(ctx, userStore, ident.NormalAddress) - if err != nil { - return ident, err - } - return ident, nil -} - -// load matching session from address from db store. -func getSessionIdByAddress(ctx context.Context, userStore *store.UserDataStore, address string) (string, error) { - // TODO: replace with userdatastore when double sessionid issue fixed - //r, err := store.ReadEntry(ctx, address, common.DATA_PUBLIC_KEY_REVERSE) - userStore.Db.SetPrefix(db.DATATYPE_USERDATA) - userStore.Db.SetSession(address) - r, err := userStore.Db.Get(ctx, storedb.PackKey(storedb.DATA_PUBLIC_KEY_REVERSE, []byte{})) - if err != nil { - return "", err - } - return string(r), nil -}