WIP: data-migration #6
							
								
								
									
										16
									
								
								cmd/main.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								cmd/main.go
									
									
									
									
									
								
							| @ -13,9 +13,7 @@ import ( | |||||||
| 	"git.defalsify.org/vise.git/lang" | 	"git.defalsify.org/vise.git/lang" | ||||||
| 	"git.grassecon.net/grassrootseconomics/sarafu-vise/config" | 	"git.grassecon.net/grassrootseconomics/sarafu-vise/config" | ||||||
| 	"git.grassecon.net/grassrootseconomics/visedriver/storage" | 	"git.grassecon.net/grassrootseconomics/visedriver/storage" | ||||||
| 	httpremote "git.grassecon.net/grassrootseconomics/sarafu-api/remote/http" | 	"git.grassecon.net/grassrootseconomics/sarafu-vise/services" | ||||||
| 	devremote "git.grassecon.net/grassrootseconomics/sarafu-api/dev" |  | ||||||
| 	"git.grassecon.net/grassrootseconomics/sarafu-api/remote" |  | ||||||
| 	"git.grassecon.net/grassrootseconomics/sarafu-vise/args" | 	"git.grassecon.net/grassrootseconomics/sarafu-vise/args" | ||||||
| 	"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers" | 	"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers" | ||||||
| ) | ) | ||||||
| @ -26,12 +24,9 @@ var ( | |||||||
| 	menuSeparator = ": " | 	menuSeparator = ": " | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| func main() { | func main() { | ||||||
| 	config.LoadConfig() | 	config.LoadConfig() | ||||||
| 
 | 
 | ||||||
| 	var accountService remote.AccountService |  | ||||||
| 	var fakeDir string |  | ||||||
| 	var connStr string | 	var connStr string | ||||||
| 	var size uint | 	var size uint | ||||||
| 	var sessionId string | 	var sessionId string | ||||||
| @ -44,7 +39,6 @@ func main() { | |||||||
| 	flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir") | 	flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir") | ||||||
| 	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") | 	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") | ||||||
| 	flag.StringVar(&connStr, "c", "", "connection string") | 	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.BoolVar(&engineDebug, "d", false, "use engine debug output") | ||||||
| 	flag.UintVar(&size, "s", 160, "max size of output") | 	flag.UintVar(&size, "s", 160, "max size of output") | ||||||
| 	flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") | 	flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") | ||||||
| @ -128,13 +122,7 @@ func main() { | |||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if fakeDir != "" { | 	accountService := services.New(ctx, menuStorageService, connData) | ||||||
| 		svc := devremote.NewDevAccountService(ctx, fakeDir).WithAutoVoucher(ctx, "FOO", 42) |  | ||||||
| 		svc.AddVoucher(ctx, "BAR") |  | ||||||
| 		accountService = svc |  | ||||||
| 	} else { |  | ||||||
| 		accountService = &httpremote.HTTPAccountService{} |  | ||||||
| 	} |  | ||||||
| 	hl, err := lhs.GetHandler(accountService) | 	hl, err := lhs.GetHandler(accountService) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) | 		fmt.Fprintf(os.Stderr, "get accounts service handler: %v\n", err) | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							| @ -3,10 +3,10 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise | |||||||
| go 1.23.4 | go 1.23.4 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d | 	git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739 | ||||||
| 	git.grassecon.net/grassrootseconomics/common v0.0.0-20250112155828-b55686e830fd | 	git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05 | ||||||
| 	git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113085238-f65abf625d34 | 	git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250115072214-bca7c5de969f | ||||||
| 	git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250113103142-5bf0a0e85893 | 	git.grassecon.net/grassrootseconomics/visedriver v0.8.0-beta.10.0.20250115003256-c0534ede1b63 | ||||||
| 	git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d | 	git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d | ||||||
| 	github.com/alecthomas/assert/v2 v2.2.2 | 	github.com/alecthomas/assert/v2 v2.2.2 | ||||||
| 	github.com/gofrs/uuid v4.4.0+incompatible | 	github.com/gofrs/uuid v4.4.0+incompatible | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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.20250115000535-e2d329b3f739 h1:w7pj1oh7jXrfajahVYU7m7AfHst4C6jNVzDVoaqJ7e8= | ||||||
| git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= | git.defalsify.org/vise.git v0.2.3-0.20250115000535-e2d329b3f739/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-20250113174703-6afccefd1f05 h1:BenzGx6aDHKDwE23/mWIFD2InYIXyzHroZWV3MF5WUk= | ||||||
| git.grassecon.net/grassrootseconomics/common v0.0.0-20250112155828-b55686e830fd/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= | git.grassecon.net/grassrootseconomics/common v0.0.0-20250113174703-6afccefd1f05/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-20250115072214-bca7c5de969f h1:FgccQi8vipX6dUt+GRiRDYHMR3UqC+plz73vw7y3fyU= | ||||||
| git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250113085238-f65abf625d34/go.mod h1:PEjJnAH5gYcjb4RRevaPCswa5VGGNqHSuyapZvIVR6w= | 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.20250113103142-5bf0a0e85893 h1:MyDINzwY1sjfXkIFoc+6T5lXF/1xdFV6yjHrpSNZzWM= | 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.20250113103142-5bf0a0e85893/go.mod h1:E6W7ZOa7ZvVr0Bc5ot0LNSwpSPYq4hXlAIvEPy3AJ7U= | 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 h1:q/NO1rEgK3pia32D/tCq5hXfEuJp84COZRwceFvy/MM= | ||||||
| git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250113103030-f0b2056fd87d/go.mod h1:AH15xABcvaJr1TCGlih3oGSuwWC0E5IdbHQwuu+E1KI= | 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= | github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= | ||||||
|  | |||||||
| @ -1139,6 +1139,8 @@ func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, in | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ValidateRecipient validates that the given input is valid.
 | // 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) { | func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||||
| 	var res resource.Result | 	var res resource.Result | ||||||
| 	store := h.userdataStore | 	store := h.userdataStore | ||||||
| @ -2248,6 +2250,23 @@ func (h *MenuHandlers) resetIncorrectPINAttempts(ctx context.Context, sessionId | |||||||
| 	return nil | 	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
 | // persistLanguageCode persists the selected ISO 639 language code
 | ||||||
| func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) error { | func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) error { | ||||||
| 	store := h.userdataStore | 	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) | 		logg.ErrorCtxf(ctx, "failed to persist language code", "key", storedb.DATA_SELECTED_LANGUAGE_CODE, "value", code, "error", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return h.persistInitialLanguageCode(ctx, sessionId, code) | ||||||
| } | } | ||||||
|  | |||||||
| @ -825,6 +825,17 @@ func TestSetLanguage(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 			// Assert that the Result FlagSet has the required flags after language switch
 | 			// Assert that the Result FlagSet has the required flags after language switch
 | ||||||
| 			assert.Equal(t, res, tt.expectedResult, "Result should match expected result") | 			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, | 				accountService: mockAccountService, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			aliasResponse := &dataserviceapi.AliasAddress{ | 			aliasResponse := &models.AliasAddress{ | ||||||
| 				Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", | 				Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										47
									
								
								handlers/event/custodial.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								handlers/event/custodial.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								handlers/event/event.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								handlers/event/event.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										205
									
								
								handlers/event/token.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								handlers/event/token.go
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								services/local.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								services/local.go
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								services/remote.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								services/remote.go
									
									
									
									
									
										Normal file
									
								
							| @ -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{} | ||||||
|  | } | ||||||
| @ -59,6 +59,8 @@ const ( | |||||||
| 	DATA_INCORRECT_PIN_ATTEMPTS | 	DATA_INCORRECT_PIN_ATTEMPTS | ||||||
| 	//ISO 639 code for the selected language.
 | 	//ISO 639 code for the selected language.
 | ||||||
| 	DATA_SELECTED_LANGUAGE_CODE | 	DATA_SELECTED_LANGUAGE_CODE | ||||||
|  | 	//ISO 639 code for the language initially selected.
 | ||||||
|  | 	DATA_INITIAL_LANGUAGE_CODE | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ import ( | |||||||
| 	visedb "git.defalsify.org/vise.git/db" | 	visedb "git.defalsify.org/vise.git/db" | ||||||
| 	storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" | 	storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" | ||||||
| 	"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
 | // 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 { | func StoreToPrefixDb(userStore *UserDataStore, pfx []byte) storedb.PrefixDb { | ||||||
| 	return storedb.NewSubPrefixDb(userStore.Db, pfx) | 	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 | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user