diff --git a/cmd/main.go b/cmd/main.go index 737cd86..81aad6f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,9 +13,7 @@ import ( "git.defalsify.org/vise.git/lang" "git.grassecon.net/grassrootseconomics/sarafu-vise/config" "git.grassecon.net/grassrootseconomics/visedriver/storage" - httpremote "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http" - devremote "git.grassecon.net/grassrootseconomics/sarafu-api/dev" - "git.grassecon.net/grassrootseconomics/sarafu-api/remote" + "git.grassecon.net/grassrootseconomics/sarafu-vise/services" "git.grassecon.net/grassrootseconomics/sarafu-vise/args" "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers" ) @@ -26,12 +24,9 @@ var ( menuSeparator = ": " ) - func main() { config.LoadConfig() - var accountService remote.AccountService - var fakeDir string var connStr string var size uint var sessionId string @@ -44,7 +39,6 @@ func main() { flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir") flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&connStr, "c", "", "connection string") - flag.StringVar(&fakeDir, "fakedir", "", "if valid path, enables fake api with fsdb backend") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") @@ -128,13 +122,7 @@ func main() { os.Exit(1) } - if fakeDir != "" { - svc := devremote.NewDevAccountService(ctx, fakeDir).WithAutoVoucher(ctx, "FOO", 42) - svc.AddVoucher(ctx, "BAR") - accountService = svc - } else { - accountService = &httpremote.HTTPAccountService{} - } + accountService := services.New(ctx, menuStorageService, connData) hl, err := lhs.GetHandler(accountService) if err != nil { fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) diff --git a/go.mod b/go.mod index 13226af..9148b5f 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise 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/visedriver v0.8.0-beta.10.0.20250113103142-5bf0a0e85893 + git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739 + git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05 + git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f + git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d github.com/alecthomas/assert/v2 v2.2.2 github.com/gofrs/uuid v4.4.0+incompatible diff --git a/go.sum b/go.sum index 2425992..c450543 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ -git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d h1:bPAOVZOX4frSGhfOdcj7kc555f8dc9DmMd2YAyC2AMw= -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/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= +git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739 h1:w7pj1oh7jXrfajahVYU7m7AfHst4C6jNVzDVoaqJ7e8= +git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05 h1:BenzGx6aDHKDwE23/mWIFD2InYIXyzHroZWV3MF5WUk= +git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= +git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f h1:FgccQi8vipX6dUt+GRiRDYHMR3UqC+plz73vw7y3fyU= +git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f/go.mod h1:tbA4whUGMUIDgQVdIW0sxWPuuXOvZRSny5zeku5hX4k= +git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63 h1:bX7klKZpX+ZZu1LKbtOXDAhV/KK0YwExehiIi0jusAM= +git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63/go.mod h1:Syw9TZyigPAM7t9FvicOm36iUnidt45f0SxzT2JniQ8= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d h1:q/NO1rEgK3pia32D/tCq5hXfEuJp84COZRwceFvy/MM= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d/go.mod h1:AH15xABcvaJr1TCGlih3oGSuwWC0E5IdbHQwuu+E1KI= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= diff --git a/handlers/application/menuhandler.go b/handlers/application/menuhandler.go index 2796231..d81ad7b 100644 --- a/handlers/application/menuhandler.go +++ b/handlers/application/menuhandler.go @@ -1139,6 +1139,8 @@ func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, in } // ValidateRecipient validates that the given input is valid. +// +// TODO: split up functino func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result store := h.userdataStore @@ -2248,6 +2250,23 @@ func (h *MenuHandlers) resetIncorrectPINAttempts(ctx context.Context, sessionId return nil } +func (h *MenuHandlers) persistInitialLanguageCode(ctx context.Context, sessionId string, code string) error { + store := h.userdataStore + _, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE) + if err == nil { + return nil + } + if !db.IsNotFound(err) { + return err + } + err = store.WriteEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE, []byte(code)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to persist initial language code", "key", storedb.DATA_INITIAL_LANGUAGE_CODE, "value", code, "error", err) + return err + } + return nil +} + // persistLanguageCode persists the selected ISO 639 language code func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) error { store := h.userdataStore @@ -2260,5 +2279,5 @@ func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) err logg.ErrorCtxf(ctx, "failed to persist language code", "key", storedb.DATA_SELECTED_LANGUAGE_CODE, "value", code, "error", err) return err } - return nil + return h.persistInitialLanguageCode(ctx, sessionId, code) } diff --git a/handlers/application/menuhandler_test.go b/handlers/application/menuhandler_test.go index eda04d6..3244ce4 100644 --- a/handlers/application/menuhandler_test.go +++ b/handlers/application/menuhandler_test.go @@ -825,6 +825,17 @@ func TestSetLanguage(t *testing.T) { // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should match expected result") + code, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SELECTED_LANGUAGE_CODE) + if err != nil { + t.Error(err) + } + + assert.Equal(t, string(code), tt.expectedResult.Content) + code, err = store.ReadEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE) + if err != nil { + t.Error(err) + } + assert.Equal(t, string(code), "eng") }) } } @@ -1698,7 +1709,7 @@ func TestValidateRecipient(t *testing.T) { accountService: mockAccountService, } - aliasResponse := &dataserviceapi.AliasAddress{ + aliasResponse := &models.AliasAddress{ Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } diff --git a/handlers/event/custodial.go b/handlers/event/custodial.go new file mode 100644 index 0000000..bcab999 --- /dev/null +++ b/handlers/event/custodial.go @@ -0,0 +1,47 @@ +package event + +import ( + "context" + "fmt" + + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" + "git.grassecon.net/grassrootseconomics/sarafu-vise/store" +) + +const ( + // TODO: consolidate with loaded flags + accountCreatedFlag = 9 +) + +// 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 (eh *EventsUpdater) handleCustodialRegistration(ctx context.Context, ev any) error { + o, ok := ev.(*apievent.EventCustodialRegistration) + if !ok { + return fmt.Errorf("invalid event for custodial registration") + } + return eh.HandleCustodialRegistration(ctx, o) +} + +func (eu *EventsUpdater) HandleCustodialRegistration(ctx context.Context, ev *apievent.EventCustodialRegistration) error { + _, userStore, err := eu.getStore(ctx) + if err != nil { + return err + } + identity, err := store.IdentityFromAddress(ctx, userStore, ev.Account) + if err != nil { + return err + } +// err = pe.Load(identity.SessionId) +// if err != nil { +// return err +// } +// st := pe.GetState() +// st.SetFlag(accountCreatedFlag) +// return pe.Save(identity.SessionId) + logg.DebugCtxf(ctx, "received custodial registration event", "identity", identity) + return nil +} diff --git a/handlers/event/event.go b/handlers/event/event.go new file mode 100644 index 0000000..bacda01 --- /dev/null +++ b/handlers/event/event.go @@ -0,0 +1,61 @@ +package event + +import ( + "context" + "fmt" + + "git.defalsify.org/vise.git/persist" + "git.defalsify.org/vise.git/logging" + "git.grassecon.net/grassrootseconomics/visedriver/storage" + "git.grassecon.net/grassrootseconomics/sarafu-vise/store" + "git.grassecon.net/grassrootseconomics/sarafu-api/remote" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" +) + +var ( + logg = logging.NewVanilla().WithDomain("sarafu-vise.handlers.event") +) + +type EventsUpdater struct { + api remote.AccountService + formatFunc func(string, int, any) string + store storage.StorageService +} + +func NewEventsUpdater(api remote.AccountService, store storage.StorageService) *EventsUpdater { + return &EventsUpdater{ + api: api, + formatFunc: func(tag string, i int, o any) string { + return fmt.Sprintf("%d %v", i, o) + }, + store: store, + } +} + +func (eu *EventsUpdater) ToEventsHandler() *apievent.EventsHandler { + eh := apievent.NewEventsHandler() + eh = eh.WithHandler(apievent.EventTokenMintTag, eu.handleTokenMint) + eh = eh.WithHandler(apievent.EventTokenTransferTag, eu.handleTokenTransfer) + eh = eh.WithHandler(apievent.EventRegistrationTag, eu.handleCustodialRegistration) + return eh +} + +func (eu *EventsUpdater) handleNoop(ctx context.Context, ev any) error { + logg.WarnCtxf(ctx, "noop event handler") + return nil +} + +func (eu *EventsUpdater) getStore(ctx context.Context) (*persist.Persister, *store.UserDataStore, error) { + userDb, err := eu.store.GetUserdataDb(ctx) + if err != nil { + return nil, nil, err + } + userStore := &store.UserDataStore{ + Db: userDb, + } + pr, err := eu.store.GetPersister(ctx) + if err != nil { + return nil, nil, err + } + return pr, userStore, nil +} diff --git a/handlers/event/token.go b/handlers/event/token.go new file mode 100644 index 0000000..ef85800 --- /dev/null +++ b/handlers/event/token.go @@ -0,0 +1,205 @@ +package event + +import ( + "context" + "fmt" + "strings" + + "git.defalsify.org/vise.git/db" + "git.grassecon.net/grassrootseconomics/sarafu-vise/store" + storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" + "git.grassecon.net/grassrootseconomics/common/identity" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" +) + +// execute all +func (eu *EventsUpdater) updateToken(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore, tokenAddress string) error { + err := eu.updateTokenList(ctx, identity, userStore) + 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 = eu.toSym(ctx, tokenAddress) + if err != nil { + return err + } + } + + err = eu.updateDefaultToken(ctx, identity, userStore, string(activeSym)) + if err != nil { + return err + } + + err = eu.updateTokenTransferList(ctx, identity, userStore) + if err != nil { + return err + } + + return nil +} + + +// set default token to given symbol. +func (eu *EventsUpdater) updateDefaultToken(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore, 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) +} + + +// handle token transfer. +// +// if from and to are NOT the same, handle code will be executed once for each side of the transfer. +func (eh *EventsUpdater) handleTokenTransfer(ctx context.Context, ev any) error { + o, ok := ev.(*apievent.EventTokenTransfer) + if !ok { + fmt.Errorf("invalid event for token transfer") + } + return eh.HandleTokenTransfer(ctx, o) +} +func (eu *EventsUpdater) HandleTokenTransfer(ctx context.Context, ev *apievent.EventTokenTransfer) error { + _, userStore, err := eu.getStore(ctx) + if err != nil { + return err + } + identity, err := store.IdentityFromAddress(ctx, userStore, ev.From) + if err != nil { + if !db.IsNotFound(err) { + return err + } + } else { + err = eu.updateToken(ctx, identity, userStore, ev.VoucherAddress) + if err != nil { + return err + } + } + + if strings.Compare(ev.To, ev.From) != 0 { + identity, err = store.IdentityFromAddress(ctx, userStore, ev.To) + if err != nil { + if !db.IsNotFound(err) { + return err + } + } else { + err = eu.updateToken(ctx, identity, userStore, ev.VoucherAddress) + if err != nil { + return err + } + } + } + + return nil +} + +// handle token mint. +func (eh *EventsUpdater) handleTokenMint(ctx context.Context, ev any) error { + o, ok := ev.(*apievent.EventTokenMint) + if !ok { + fmt.Errorf("invalid event for token mint") + } + return eh.HandleTokenMint(ctx, o) +} +func (eu *EventsUpdater) HandleTokenMint(ctx context.Context, ev *apievent.EventTokenMint) error { + _, userStore, err := eu.getStore(ctx) + if err != nil { + return err + } + identity, err := store.IdentityFromAddress(ctx, userStore, ev.To) + if err != nil { + if !db.IsNotFound(err) { + return err + } + } else { + err = eu.updateToken(ctx, identity, userStore, ev.VoucherAddress) + if err != nil { + return err + } + } + return nil +} + +// use api to resolve address to token symbol. +func (eu *EventsUpdater) toSym(ctx context.Context, address string) ([]byte, error) { + voucherData, err := eu.api.VoucherData(ctx, address) + if err != nil { + return nil, err + } + return []byte(voucherData.TokenSymbol), nil +} + +// refresh and store token list. +func (eu *EventsUpdater) updateTokenList(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore) error { + holdings, err := eu.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 +} + +// refresh and store transaction history. +func (eu *EventsUpdater) updateTokenTransferList(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore) error { + var r []string + + txs, err := eu.api.FetchTransactions(ctx, identity.ChecksumAddress) + if err != nil { + return err + } + + for i, tx := range(txs) { + r = append(r, eu.formatFunc(apievent.EventTokenTransferTag, i, tx)) + } + + s := strings.Join(r, "\n") + + return userStore.WriteEntry(ctx, identity.SessionId, storedb.DATA_TRANSACTIONS, []byte(s)) +} + +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/services/local.go b/services/local.go new file mode 100644 index 0000000..e79c8fb --- /dev/null +++ b/services/local.go @@ -0,0 +1,50 @@ +// +build !online + +package services + +import ( + "fmt" + "context" + + "git.grassecon.net/grassrootseconomics/visedriver/storage" + devremote "git.grassecon.net/grassrootseconomics/sarafu-api/dev" + "git.grassecon.net/grassrootseconomics/sarafu-api/remote" + apievent "git.grassecon.net/grassrootseconomics/sarafu-api/event" + "git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/event" +) + +type localEmitter struct { + h *apievent.EventsHandler +} + +func (d *localEmitter) emit(ctx context.Context, msg apievent.Msg) error { + var err error + if msg.Typ == apievent.EventTokenTransferTag { + tx, ok := msg.Item.(devremote.Tx) + if !ok { + return fmt.Errorf("not a valid tx") + } + ev := tx.ToTransferEvent() + err = d.h.Handle(ctx, apievent.EventTokenTransferTag, &ev) + } else if msg.Typ == apievent.EventRegistrationTag { + acc, ok := msg.Item.(devremote.Account) + if !ok { + return fmt.Errorf("not a valid tx") + } + ev := acc.ToRegistrationEvent() + err = d.h.Handle(ctx, apievent.EventRegistrationTag, &ev) + } + return err +} + +func New(ctx context.Context, storageService storage.StorageService, conn storage.ConnData) remote.AccountService { + svc := devremote.NewDevAccountService(ctx, storageService) + svc = svc.WithAutoVoucher(ctx, "FOO", 42) + eu := event.NewEventsUpdater(svc, storageService) + emitter := &localEmitter{ + h: eu.ToEventsHandler(), + } + svc = svc.WithEmitter(emitter.emit) + svc.AddVoucher(ctx, "BAR") + return svc +} diff --git a/services/remote.go b/services/remote.go new file mode 100644 index 0000000..2d31217 --- /dev/null +++ b/services/remote.go @@ -0,0 +1,15 @@ +// +build online + +package services + +import ( + "context" + + "git.grassecon.net/grassrootseconomics/visedriver/storage" + "git.grassecon.net/grassrootseconomics/sarafu-api/remote" + httpremote "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http" +) + +func New(ctx context.Context, storageService storage.StorageService, conn storage.ConnData) remote.AccountService { + return &httpremote.HTTPAccountService{} +} diff --git a/store/db/db.go b/store/db/db.go index b70cc5e..2ccd9e6 100644 --- a/store/db/db.go +++ b/store/db/db.go @@ -59,6 +59,8 @@ const ( DATA_INCORRECT_PIN_ATTEMPTS //ISO 639 code for the selected language. DATA_SELECTED_LANGUAGE_CODE + //ISO 639 code for the language initially selected. + DATA_INITIAL_LANGUAGE_CODE ) const ( diff --git a/store/user_store.go b/store/user_store.go index 920d32e..70e2539 100644 --- a/store/user_store.go +++ b/store/user_store.go @@ -6,6 +6,8 @@ import ( visedb "git.defalsify.org/vise.git/db" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" + "git.grassecon.net/grassrootseconomics/common/hex" + "git.grassecon.net/grassrootseconomics/common/identity" ) // TODO: Rename interface, "datastore" is redundant naming and too general @@ -39,3 +41,37 @@ func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, ty func StoreToPrefixDb(userStore *UserDataStore, pfx []byte) storedb.PrefixDb { return storedb.NewSubPrefixDb(userStore.Db, pfx) } + +// 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 *UserDataStore, address string) (identity.Identity, error) { + var err error + var ident identity.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 *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(visedb.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 +}