dev-api-aliases #5

Merged
lash merged 6 commits from dev-api-aliases into master 2025-01-21 07:17:09 +01:00
3 changed files with 170 additions and 110 deletions

View File

@ -12,19 +12,20 @@ import (
"strings"
"time"
"github.com/gofrs/uuid"
"git.defalsify.org/vise.git/logging"
"git.defalsify.org/vise.git/db"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/sarafu-api/event"
"git.defalsify.org/vise.git/logging"
"git.grassecon.net/grassrootseconomics/common/phone"
"git.grassecon.net/grassrootseconomics/sarafu-api/event"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/visedriver/storage"
"github.com/gofrs/uuid"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
var (
logg = logging.NewVanilla().WithDomain("sarafu-api.devapi")
aliasRegex = regexp.MustCompile("^\\+?[a-zA-Z0-9\\-_]+$")
searchDomain = ".sarafu.local"
Outdated
Review

Export please

Export please
@lash Should be moved to: https://git.grassecon.net/grassrootseconomics/common?
Outdated
Review

no its fine here, it's for dev only anyway.

no its fine here, it's for dev only anyway.
)
const (
@ -32,6 +33,7 @@ const (
hashLen int = 32
defaultDecimals = 6
zeroAddress string = "0x0000000000000000000000000000000000000000"
defaultVoucherBalance float64 = 500.00
)
type Tx struct {
@ -104,7 +106,6 @@ type DevAccountService struct {
defaultAccount string
emitterFunc event.EmitterFunc
pfx []byte
Review

remove commentedp lease

remove commentedp lease
// accountsSession map[string]string
}
func NewDevAccountService(ctx context.Context, ss storage.StorageService) *DevAccountService {
@ -183,6 +184,15 @@ func (das *DevAccountService) loadTx(ctx context.Context, hsh string, v []byte)
return nil
}
func (das *DevAccountService) loadAlias(ctx context.Context, alias string, key []byte) error {
result, err := das.db.Get(ctx, key)
if err != nil {
return err
}
das.accountsAlias[alias] = strings.ReplaceAll(string(result), `"`, "")
return nil
}
func (das *DevAccountService) loadItem(ctx context.Context, k []byte, v []byte) error {
var err error
s := string(k)
@ -194,6 +204,8 @@ func (das *DevAccountService) loadItem(ctx context.Context, k []byte, v []byte)
err = das.loadAccount(ctx, ss[1], v)
} else if ss[0] == "tx" {
err = das.loadTx(ctx, ss[1], v)
} else if ss[0] == "alias" {
err = das.loadAlias(ctx, ss[1], k)
} else {
logg.ErrorCtxf(ctx, "unknown double underscore key", "key", ss[0])
}
@ -225,7 +237,7 @@ func (das *DevAccountService) loadAll(ctx context.Context) error {
}
func (das *DevAccountService) indexAll(ctx context.Context) error {
for k, v := range(das.txs) {
for k, v := range das.txs {
acc := das.accounts[v.From]
acc.Txs = append(acc.Txs, k)
logg.TraceCtxf(ctx, "add tx to sender index", "from", v.From, "tx", k)
@ -295,7 +307,7 @@ func (das *DevAccountService) CheckBalance(ctx context.Context, publicKey string
}
func (das *DevAccountService) balanceAuto(ctx context.Context, pubKey string) error {
for _, v := range(das.autoVouchers) {
for _, v := range das.autoVouchers {
voucher, ok := das.vouchers[v]
if !ok {
return fmt.Errorf("balance auto voucher set but not resolved: %s", v)
@ -312,6 +324,10 @@ func (das *DevAccountService) balanceAuto(ctx context.Context, pubKey string) er
return nil
}
func (das *DevAccountService) GetAliases(ctx context.Context) map[string]string {
return das.accountsAlias
}
func (das *DevAccountService) saveAccount(ctx context.Context, acc Account) error {
if das.db == nil {
return nil
@ -326,6 +342,27 @@ func (das *DevAccountService) saveAccount(ctx context.Context, acc Account) erro
return das.db.Put(ctx, []byte(k), v)
}
func (das *DevAccountService) saveAlias(ctx context.Context, alias map[string]string) error {
if das.db == nil {
return fmt.Errorf("Db cannot be nil")
}
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return fmt.Errorf("unresolved session id")
Outdated
Review

This is probably not correct? Alias is bound to a session, right?

This is probably not correct? Alias is bound to a session, right?

Yeah that's right.Resolved by: ed549cba70

Yeah that's right.Resolved by: ed549cba7046818638f28ce28fae15c3eb344bc8
}
for k, v := range alias {
k_ := das.prefixKeyFor("alias", k)
v_, err := json.Marshal(v)
if err != nil {
return err
}
das.db.SetSession(sessionId)
das.db.SetPrefix(db.DATATYPE_USERDATA)
return das.db.Put(ctx, []byte(k_), v_)
}
return nil
}
func (das *DevAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) {
var b [pubKeyLen]byte
uid, err := uuid.NewV4()
@ -392,22 +429,20 @@ func (das *DevAccountService) TrackAccountStatus(ctx context.Context, publicKey
func (das *DevAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
var holdings []dataserviceapi.TokenHoldings
acc, ok := das.accounts[publicKey]
_, ok := das.accounts[publicKey]
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", publicKey)
Outdated
Review

Why hardcoded number?

Why hardcoded number?
}
for k, v := range(acc.Balances) {
voucher, ok := das.vouchers[k]
if !ok {
return nil, fmt.Errorf("voucher has balance but object not found: %v", k)
}
//TODO: Iterate over the account acc.Balances object
for _, voucher := range das.vouchers {
holdings = append(holdings, dataserviceapi.TokenHoldings{
ContractAddress: voucher.Address,
TokenSymbol: voucher.Symbol,
TokenDecimals: strconv.Itoa(voucher.Decimals),
Balance: strconv.Itoa(v),
Balance: strconv.Itoa(int(defaultVoucherBalance)),
})
}
return holdings, nil
}
@ -417,7 +452,7 @@ func (das *DevAccountService) FetchTransactions(ctx context.Context, publicKey s
if !ok {
return nil, fmt.Errorf("account not found (publickey): %v", publicKey)
}
for i, v := range(acc.Txs) {
for i, v := range acc.Txs {
mytx := das.txs[v]
if i == 10 {
break
@ -456,7 +491,6 @@ func (das *DevAccountService) VoucherData(ctx context.Context, address string) (
SinkAddress: voucher.Sink,
TokenCommodity: voucher.Commodity,
TokenLocation: voucher.Location,
}, nil
}
@ -568,12 +602,22 @@ func (das *DevAccountService) applyPhoneAlias(ctx context.Context, publicKey str
func (das *DevAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
var alias string
uid, err := uuid.NewV4()
if !aliasRegex.MatchString(hint) {
return nil, fmt.Errorf("alias hint does not match: %s", publicKey)
}
acc, ok := das.accounts[publicKey]
if !ok {
return nil, fmt.Errorf("address %s not found", publicKey)
//Handle accounts created via the api
acc = Account{
Track: uid.String(),
Address: publicKey,
}
err = das.saveAccount(ctx, acc)
if err != nil {
return nil, err
}
das.accounts[publicKey] = acc
}
alias = hint
isPhone, err := das.applyPhoneAlias(ctx, publicKey, alias)
@ -592,7 +636,12 @@ func (das *DevAccountService) RequestAlias(ctx context.Context, publicKey string
alias += "x"
}
acc.Alias = alias
alias = alias + searchDomain
das.accountsAlias[alias] = publicKey
err := das.saveAlias(ctx, map[string]string{alias: publicKey})
if err != nil {
return nil, fmt.Errorf("Failed to save the account alias with error: %s", err.Error())
}
}
logg.DebugCtxf(ctx, "set alias", "addr", publicKey, "alias", alias)
return &models.RequestAliasResult{

View File

@ -9,6 +9,7 @@ import (
func TestApiRequestAlias(t *testing.T) {
ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", "+25471234565")
storageService := mocks.NewMemStorageService(ctx)
svc := NewDevAccountService(ctx, storageService)
ra, err := svc.CreateAccount(ctx)
@ -39,6 +40,7 @@ func TestApiRequestAlias(t *testing.T) {
if err != nil {
t.Fatal(err)
}
alias = "foo.sarafu.local"
if rb.Alias != alias {
t.Fatalf("expected '%s', got '%s'", alias, rb.Alias)
}
@ -56,12 +58,12 @@ func TestApiRequestAlias(t *testing.T) {
t.Fatal(err)
}
addr = ra.PublicKey
alias = "foox"
rb, err = svc.RequestAlias(ctx, addr, alias)
if err != nil {
t.Fatal(err)
}
alias = "foox"
alias = "foox.sarafu.local"
if rb.Alias != alias {
t.Fatalf("expected '%s', got '%s'", alias, rb.Alias)
}

View File

@ -3,20 +3,26 @@ package http
import (
"bytes"
"context"
"fmt"
"encoding/json"
"errors"
"io"
"log"
"net/http"
"net/url"
"regexp"
"git.grassecon.net/grassrootseconomics/sarafu-api/config"
"git.grassecon.net/grassrootseconomics/sarafu-api/dev"
"git.grassecon.net/grassrootseconomics/sarafu-api/models"
"git.grassecon.net/grassrootseconomics/visedriver/testutil/mocks"
"github.com/grassrootseconomics/eth-custodial/pkg/api"
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
)
var (
aliasRegex = regexp.MustCompile("^\\+?[a-zA-Z0-9\\-_]+$")
Outdated
Review

THe searchdomain should not be necessary here

THe searchdomain should not be necessary here
)
type HTTPAccountService struct {
}
@ -220,8 +226,11 @@ func (as *HTTPAccountService) CheckAliasAddress(ctx context.Context, alias strin
return &r, err
}
// TODO: Use actual custodial api to request available alias
func (as *HTTPAccountService) RequestAlias(ctx context.Context, publicKey string, hint string) (*models.RequestAliasResult, error) {
Review

Can't we just point this to devapi.RequestAlias?

Can't we just point this to devapi.RequestAlias?
Review

@lash Nice catch.Actually that brings up an idea,could we use the same implementation of the devapi to resolve the alias' address given that the api always returns the same address?

@lash Nice catch.Actually that brings up an idea,could we use the same implementation of the devapi to resolve the alias' address given that the api always returns the same address?
Review

yes for sure, while we wait or the api implementation.

yes for sure, while we wait or the api implementation.
return nil, fmt.Errorf("not yet implemented")
storageService := mocks.NewMemStorageService(ctx)
svc := dev.NewDevAccountService(ctx, storageService)
return svc.RequestAlias(ctx, publicKey, hint)
}
// TODO: remove eth-custodial api dependency