add-space-after-colon #211
@ -7,7 +7,6 @@ import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
@ -45,14 +44,14 @@ type atRequestParser struct{}
 | 
			
		||||
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 | 
			
		||||
	rqv, ok := rq.(*http.Request)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		log.Printf("got an invalid request:", rq)
 | 
			
		||||
		logg.Warnf("got an invalid request", "req", rq)
 | 
			
		||||
		return "", handlers.ErrInvalidRequest
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Capture body (if any) for logging
 | 
			
		||||
	body, err := io.ReadAll(rqv.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("failed to read request body:", err)
 | 
			
		||||
		logg.Warnf("failed to read request body", "err", err)
 | 
			
		||||
		return "", fmt.Errorf("failed to read request body: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	// Reset the body for further reading
 | 
			
		||||
@ -62,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 | 
			
		||||
	bodyLog := map[string]string{"body": string(body)}
 | 
			
		||||
	logBytes, err := json.Marshal(bodyLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("failed to marshal request body:", err)
 | 
			
		||||
		logg.Warnf("failed to marshal request body", "err", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("Received request:", string(logBytes))
 | 
			
		||||
		logg.Debugf("received request", "bytes", logBytes)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := rqv.ParseForm(); err != nil {
 | 
			
		||||
		log.Printf("failed to parse form data: %v", err)
 | 
			
		||||
		logg.Warnf("failed to parse form data", "err", err)
 | 
			
		||||
		return "", fmt.Errorf("failed to parse form data: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -79,7 +78,7 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 | 
			
		||||
 | 
			
		||||
	formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Error: %v\n", err)
 | 
			
		||||
		logg.Warnf("failed to format phone number", "err", err)
 | 
			
		||||
		return "", fmt.Errorf("failed to format number")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										54
									
								
								common/db.go
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								common/db.go
									
									
									
									
									
								
							@ -7,43 +7,83 @@ import (
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DataType is a subprefix value used in association with vise/db.DATATYPE_USERDATA. 
 | 
			
		||||
//
 | 
			
		||||
// All keys are used only within the context of a single account. Unless otherwise specified, the user context is the session id.
 | 
			
		||||
//
 | 
			
		||||
// * The first byte is vise/db.DATATYPE_USERDATA
 | 
			
		||||
// * The last 2 bytes are the DataTyp value, big-endian.
 | 
			
		||||
// * The intermediate bytes are the id of the user context.
 | 
			
		||||
//
 | 
			
		||||
// All values are strings
 | 
			
		||||
type DataTyp uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DATA_ACCOUNT DataTyp = iota
 | 
			
		||||
	DATA_ACCOUNT_CREATED
 | 
			
		||||
	DATA_TRACKING_ID
 | 
			
		||||
	// API Tracking id to follow status of account creation
 | 
			
		||||
	DATA_TRACKING_ID = iota
 | 
			
		||||
	// EVM address returned from API on account creation
 | 
			
		||||
	DATA_PUBLIC_KEY
 | 
			
		||||
	DATA_CUSTODIAL_ID
 | 
			
		||||
	// Currently active PIN used to authenticate ussd state change requests
 | 
			
		||||
	DATA_ACCOUNT_PIN
 | 
			
		||||
	DATA_ACCOUNT_STATUS
 | 
			
		||||
	// The first name of the user
 | 
			
		||||
	DATA_FIRST_NAME
 | 
			
		||||
	// The last name of the user
 | 
			
		||||
	DATA_FAMILY_NAME
 | 
			
		||||
	// The year-of-birth of the user
 | 
			
		||||
	DATA_YOB
 | 
			
		||||
	// The location of the user
 | 
			
		||||
	DATA_LOCATION
 | 
			
		||||
	// The gender of the user
 | 
			
		||||
	DATA_GENDER
 | 
			
		||||
	// The offerings description of the user
 | 
			
		||||
	DATA_OFFERINGS
 | 
			
		||||
	// The ethereum address of the recipient of an ongoing send request
 | 
			
		||||
	DATA_RECIPIENT
 | 
			
		||||
	// The voucher value amount of an ongoing send request
 | 
			
		||||
	DATA_AMOUNT
 | 
			
		||||
	// A general swap field for temporary values
 | 
			
		||||
	DATA_TEMPORARY_VALUE
 | 
			
		||||
	// Currently active voucher symbol of user
 | 
			
		||||
	DATA_ACTIVE_SYM
 | 
			
		||||
	// Voucher balance of user's currently active voucher
 | 
			
		||||
	DATA_ACTIVE_BAL
 | 
			
		||||
	// String boolean indicating whether use of PIN is blocked
 | 
			
		||||
	DATA_BLOCKED_NUMBER
 | 
			
		||||
	// Reverse mapping of a user's evm address to a session id.
 | 
			
		||||
	DATA_PUBLIC_KEY_REVERSE
 | 
			
		||||
	// Decimal count of the currently active voucher
 | 
			
		||||
	DATA_ACTIVE_DECIMAL
 | 
			
		||||
	// EVM address of the currently active voucher
 | 
			
		||||
	DATA_ACTIVE_ADDRESS
 | 
			
		||||
	// Start the sub prefix data at 256 (0x0100)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// List of valid voucher symbols in the user context.
 | 
			
		||||
	DATA_VOUCHER_SYMBOLS DataTyp = 256 + iota
 | 
			
		||||
	// List of voucher balances for vouchers valid in the user context.
 | 
			
		||||
	DATA_VOUCHER_BALANCES
 | 
			
		||||
	// List of voucher decimal counts for vouchers valid in the user context.
 | 
			
		||||
	DATA_VOUCHER_DECIMALS
 | 
			
		||||
	// List of voucher EVM addresses for vouchers valid in the user context.
 | 
			
		||||
	DATA_VOUCHER_ADDRESSES
 | 
			
		||||
	DATA_TX_SENDERS
 | 
			
		||||
	// List of senders for valid transactions in the user context.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DATA_TX_SENDERS = 512 + iota
 | 
			
		||||
	// List of recipients for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_RECIPIENTS
 | 
			
		||||
	// List of voucher values for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_VALUES
 | 
			
		||||
	// List of voucher EVM addresses for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_ADDRESSES
 | 
			
		||||
	// List of valid transaction hashes in the user context.
 | 
			
		||||
	DATA_TX_HASHES
 | 
			
		||||
	// List of transaction dates for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_DATES
 | 
			
		||||
	// List of voucher symbols for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_SYMBOLS
 | 
			
		||||
	// List of voucher decimal counts for valid transactions in the user context.
 | 
			
		||||
	DATA_TX_DECIMALS
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ type UserDataStore struct {
 | 
			
		||||
func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) {
 | 
			
		||||
	store.SetPrefix(db.DATATYPE_USERDATA)
 | 
			
		||||
	store.SetSession(sessionId)
 | 
			
		||||
	k := PackKey(typ, []byte(sessionId))
 | 
			
		||||
	k := ToBytes(typ)
 | 
			
		||||
	return store.Get(ctx, k)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,6 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ
 | 
			
		||||
func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error {
 | 
			
		||||
	store.SetPrefix(db.DATATYPE_USERDATA)
 | 
			
		||||
	store.SetSession(sessionId)
 | 
			
		||||
	k := PackKey(typ, []byte(sessionId))
 | 
			
		||||
	k := ToBytes(typ)
 | 
			
		||||
	return store.Put(ctx, k, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								debug/cap.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								debug/cap.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
package debug
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	DebugCap uint32
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										84
									
								
								debug/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								debug/db.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
package debug
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/common"
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	dbTypStr map[common.DataTyp]string = make(map[common.DataTyp]string)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type KeyInfo struct {
 | 
			
		||||
	SessionId string
 | 
			
		||||
	Typ uint8
 | 
			
		||||
	SubTyp common.DataTyp
 | 
			
		||||
	Label string
 | 
			
		||||
	Description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (k KeyInfo) String() string {
 | 
			
		||||
	v := uint16(k.SubTyp)
 | 
			
		||||
	s := subTypToString(k.SubTyp)
 | 
			
		||||
	if s == "" {
 | 
			
		||||
		v = uint16(k.Typ)
 | 
			
		||||
		s = typToString(k.Typ)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("Session Id: %s\nTyp: %s (%d)\n", k.SessionId, s, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) {
 | 
			
		||||
	o := KeyInfo{}
 | 
			
		||||
	b := []byte(sessionId)
 | 
			
		||||
 | 
			
		||||
	if len(k) <= len(b) {
 | 
			
		||||
		return o, fmt.Errorf("storage key missing")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o.SessionId = sessionId
 | 
			
		||||
 | 
			
		||||
	o.Typ = uint8(k[0])
 | 
			
		||||
	k = k[1:]
 | 
			
		||||
	o.SessionId = string(k[:len(b)])
 | 
			
		||||
	k = k[len(b):]
 | 
			
		||||
 | 
			
		||||
	if o.Typ == db.DATATYPE_USERDATA {
 | 
			
		||||
		if len(k) == 0 {
 | 
			
		||||
			return o, fmt.Errorf("missing subtype key")
 | 
			
		||||
		}
 | 
			
		||||
		v := binary.BigEndian.Uint16(k[:2])
 | 
			
		||||
		o.SubTyp = common.DataTyp(v)
 | 
			
		||||
		o.Label = subTypToString(o.SubTyp)
 | 
			
		||||
		k = k[2:]
 | 
			
		||||
	} else {
 | 
			
		||||
		o.Label = typToString(o.Typ)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(k) != 0 {
 | 
			
		||||
		return o, fmt.Errorf("excess key information")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return o, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FromKey(k []byte) (KeyInfo, error) {
 | 
			
		||||
	o := KeyInfo{}
 | 
			
		||||
 | 
			
		||||
	if len(k) < 4 {
 | 
			
		||||
		return o, fmt.Errorf("insufficient key length")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sessionIdBytes := k[1:len(k)-2]
 | 
			
		||||
	return ToKeyInfo(k, string(sessionIdBytes))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func subTypToString(v common.DataTyp) string {
 | 
			
		||||
	return dbTypStr[v + db.DATATYPE_USERDATA + 1]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func typToString(v uint8) string {
 | 
			
		||||
	return dbTypStr[common.DataTyp(uint16(v))]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								debug/db_debug.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								debug/db_debug.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
// +build debugdb
 | 
			
		||||
 | 
			
		||||
package debug
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	DebugCap |= 1
 | 
			
		||||
	dbTypStr[db.DATATYPE_STATE] = "internal state"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account created"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_LOCATION] = "location"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_GENDER] = "gender"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_OFFERINGS] = "offerings"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_RECIPIENT] = "recipient"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_AMOUNT] = "amount"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_SYM] = "active sym"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_BAL] = "active bal"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_ADDRESS] = "active address"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_SYMBOLS] = "voucher symbols"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_BALANCES] = "voucher balances"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_DECIMALS] = "voucher decimals"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_ADDRESSES] = "voucher addresses"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SENDERS] = "tx senders"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_RECIPIENTS] = "tx recipients"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_VALUES] = "tx values"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_ADDRESSES] = "tx addresses"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_HASHES] = "tx hashes"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DATES] = "tx dates"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SYMBOLS] = "tx symbols"
 | 
			
		||||
	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DECIMALS] = "tx decimals"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								debug/db_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								debug/db_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
package debug
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/common"
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDebugDbSubKeyInfo(t *testing.T) {
 | 
			
		||||
	s := "foo"
 | 
			
		||||
	b := []byte{0x20}
 | 
			
		||||
	b = append(b, []byte(s)...)
 | 
			
		||||
	b = append(b, []byte{0x00, 0x02}...)
 | 
			
		||||
	r, err := ToKeyInfo(b, s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if r.SessionId != s {
 | 
			
		||||
		t.Fatalf("expected %s, got %s", s, r.SessionId)
 | 
			
		||||
	}
 | 
			
		||||
	if r.Typ != 32 {
 | 
			
		||||
		t.Fatalf("expected 64, got %d", r.Typ)
 | 
			
		||||
	}
 | 
			
		||||
	if r.SubTyp != 2 {
 | 
			
		||||
		t.Fatalf("expected 2, got %d", r.SubTyp)
 | 
			
		||||
	}
 | 
			
		||||
	if DebugCap & 1 > 0 {
 | 
			
		||||
		if r.Label != "tracking id" {
 | 
			
		||||
			t.Fatalf("expected 'tracking id', got '%s'", r.Label)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDebugDbKeyInfo(t *testing.T) {
 | 
			
		||||
	s := "bar"
 | 
			
		||||
	b := []byte{0x10}
 | 
			
		||||
	b = append(b, []byte(s)...)
 | 
			
		||||
	r, err := ToKeyInfo(b, s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if r.SessionId != s {
 | 
			
		||||
		t.Fatalf("expected %s, got %s", s, r.SessionId)
 | 
			
		||||
	}
 | 
			
		||||
	if r.Typ != 16 {
 | 
			
		||||
		t.Fatalf("expected 16, got %d", r.Typ)
 | 
			
		||||
	}
 | 
			
		||||
	if DebugCap & 1 > 0 {
 | 
			
		||||
		if r.Label != "internal state" {
 | 
			
		||||
			t.Fatalf("expected 'internal_state', got '%s'", r.Label)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDebugDbKeyInfoRestore(t *testing.T) {
 | 
			
		||||
	s := "bar"
 | 
			
		||||
	b := []byte{db.DATATYPE_USERDATA}
 | 
			
		||||
	b = append(b, []byte(s)...)
 | 
			
		||||
	k := common.ToBytes(common.DATA_ACTIVE_SYM)
 | 
			
		||||
	b = append(b, k...)
 | 
			
		||||
 | 
			
		||||
	r, err := ToKeyInfo(b, s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	if r.SessionId != s {
 | 
			
		||||
		t.Fatalf("expected %s, got %s", s, r.SessionId)
 | 
			
		||||
	}
 | 
			
		||||
	if r.Typ != 32 {
 | 
			
		||||
		t.Fatalf("expected 32, got %d", r.Typ)
 | 
			
		||||
	}
 | 
			
		||||
	if DebugCap & 1 > 0 {
 | 
			
		||||
		if r.Label != "active sym" {
 | 
			
		||||
			t.Fatalf("expected 'active sym', got '%s'", r.Label)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								devtools/gen/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								devtools/gen/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"crypto/sha1"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/config"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/storage"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/initializers"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg      = logging.NewVanilla()
 | 
			
		||||
	scriptDir = path.Join("services", "registration")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	initializers.LoadEnvVariables()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	config.LoadConfig()
 | 
			
		||||
 | 
			
		||||
	var dbDir string
 | 
			
		||||
	var sessionId string
 | 
			
		||||
	var database string
 | 
			
		||||
	var engineDebug bool
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
 | 
			
		||||
	flag.StringVar(&database, "db", "gdbm", "database to be used")
 | 
			
		||||
	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
 | 
			
		||||
	flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	ctx = context.WithValue(ctx, "SessionId", sessionId)
 | 
			
		||||
	ctx = context.WithValue(ctx, "Database", database)
 | 
			
		||||
 | 
			
		||||
	resourceDir := scriptDir
 | 
			
		||||
	menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
 | 
			
		||||
 | 
			
		||||
	store, err := menuStorageService.GetUserdataDb(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	userStore := common.UserDataStore{store}
 | 
			
		||||
 | 
			
		||||
	h := sha1.New()
 | 
			
		||||
	h.Write([]byte(sessionId))
 | 
			
		||||
	address := h.Sum(nil)
 | 
			
		||||
	addressString := fmt.Sprintf("%x", address)
 | 
			
		||||
 | 
			
		||||
	err = userStore.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(addressString))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = userStore.WriteEntry(ctx, addressString, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = store.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								devtools/store/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								devtools/store/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/config"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/initializers"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/storage"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/debug"
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg      = logging.NewVanilla()
 | 
			
		||||
	scriptDir = path.Join("services", "registration")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	initializers.LoadEnvVariables()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	config.LoadConfig()
 | 
			
		||||
 | 
			
		||||
	var dbDir string
 | 
			
		||||
	var sessionId string
 | 
			
		||||
	var database string
 | 
			
		||||
	var engineDebug bool
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
 | 
			
		||||
	flag.StringVar(&database, "db", "gdbm", "database to be used")
 | 
			
		||||
	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
 | 
			
		||||
	flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	ctx = context.WithValue(ctx, "SessionId", sessionId)
 | 
			
		||||
	ctx = context.WithValue(ctx, "Database", database)
 | 
			
		||||
 | 
			
		||||
	resourceDir := scriptDir
 | 
			
		||||
	menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
 | 
			
		||||
 | 
			
		||||
	store, err := menuStorageService.GetUserdataDb(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d, err := store.Dump(ctx, []byte(sessionId))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for true {
 | 
			
		||||
		k, v := d.Next(ctx)
 | 
			
		||||
		if k == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		o, err := debug.FromKey(k)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("%vValue: %v\n\n", o, v)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = store.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								doc/data.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								doc/data.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
# Internals
 | 
			
		||||
 | 
			
		||||
## Version
 | 
			
		||||
 | 
			
		||||
This document describes component versions:
 | 
			
		||||
 | 
			
		||||
* `urdt-ussd` `v0.5.0-beta`
 | 
			
		||||
* `go-vise` `v0.2.2`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## User profile data
 | 
			
		||||
 | 
			
		||||
All user profile items are stored under keys matching the user's session id, prefixed with the 8-bit value `git.defalsify.org/vise.git/db.DATATYPE_USERDATA` (32), and followed with a 16-big big-endian value subprefix.
 | 
			
		||||
 | 
			
		||||
For example, given the sessionId `+254123` and the key `git.grassecon.net/urdt-ussd/common.DATA_PUBLIC_KEY` (2) will be stored under the key:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
0x322b3235343132330002
 | 
			
		||||
 | 
			
		||||
prefix   sessionid       subprefix
 | 
			
		||||
32       2b323534313233  0002
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Sub-prefixes
 | 
			
		||||
 | 
			
		||||
All sub-prefixes are defined as constants in the `git.grassecon.net/urdt-ussd/common` package. The constant names have the prefix `DATA_`
 | 
			
		||||
 | 
			
		||||
Please refer to inline godoc documentation for the `git.grassecon.net/urdt-ussd/common` package for details on each data item.
 | 
			
		||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd
 | 
			
		||||
go 1.23.0
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf
 | 
			
		||||
	git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80
 | 
			
		||||
	github.com/alecthomas/assert/v2 v2.2.2
 | 
			
		||||
	github.com/gofrs/uuid v4.4.0+incompatible
 | 
			
		||||
	github.com/grassrootseconomics/eth-custodial v1.3.0-beta
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.sum
									
									
									
									
									
								
							@ -1,9 +1,5 @@
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede h1:BtooQZJDYEgeRKqv8RSxYSbW5jh0UIFJ6zRMEZZn0sc=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf h1:K58RcCiBZSmfU7zwg4nCHzY5ikw+wyfFoDU+4SXkarU=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
 | 
			
		||||
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
 | 
			
		||||
 | 
			
		||||
@ -218,7 +218,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte)
 | 
			
		||||
		return res, fmt.Errorf("missing session")
 | 
			
		||||
	}
 | 
			
		||||
	store := h.userdataStore
 | 
			
		||||
	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED)
 | 
			
		||||
	_, err = store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if db.IsNotFound(err) {
 | 
			
		||||
			logg.InfoCtxf(ctx, "Creating an account because it doesn't exist")
 | 
			
		||||
 | 
			
		||||
@ -115,7 +115,8 @@ func(tdb *ThreadGdbmDb) Close() error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) {
 | 
			
		||||
	logg.Warnf("method not implemented for thread gdbm db")
 | 
			
		||||
	return nil, nil
 | 
			
		||||
func(tdb *ThreadGdbmDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) {
 | 
			
		||||
	tdb.reserve()
 | 
			
		||||
	defer tdb.release()
 | 
			
		||||
	return tdb.db.Dump(ctx, key)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "input": "0",
 | 
			
		||||
                        "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No"
 | 
			
		||||
                        "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "input": "0",
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "input": "0",
 | 
			
		||||
                        "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No"
 | 
			
		||||
                        "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "input": "1",
 | 
			
		||||
 | 
			
		||||
@ -1 +1,2 @@
 | 
			
		||||
Do you agree to terms and conditions?
 | 
			
		||||
Do you agree to terms and conditions?
 | 
			
		||||
https://grassecon.org/pages/terms-and-conditions
 | 
			
		||||
 | 
			
		||||
@ -1 +1,2 @@
 | 
			
		||||
Kwa kutumia hii huduma umekubali sheria na masharti?
 | 
			
		||||
Kwa kutumia hii huduma umekubali sheria na masharti?
 | 
			
		||||
https://grassecon.org/pages/terms-and-conditions
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user