forked from urdt/ussd
Compare commits
19 Commits
add-space-
...
master
Author | SHA1 | Date | |
---|---|---|---|
8cf4848b45 | |||
9f6c0a1111 | |||
|
1ab49647f6 | ||
|
8d4d8a48e0 | ||
|
c820e89cb7 | ||
|
604c16ec90 | ||
|
a3e821fb16 | ||
|
890f50704f | ||
|
3416fdf50c | ||
|
ff943a125c | ||
|
c2a4efde2b | ||
|
c9deca1180 | ||
|
6d4f3109f8 | ||
|
35cf3a1cd1 | ||
|
1a782c1db9 | ||
|
3d2ca606ca | ||
584d02db29 | |||
5937c6bf5c | |||
|
10b3083647 |
@ -7,7 +7,6 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -30,10 +29,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
build = "dev"
|
|
||||||
menuSeparator = ": "
|
build = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -45,14 +44,14 @@ type atRequestParser struct{}
|
|||||||
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
||||||
rqv, ok := rq.(*http.Request)
|
rqv, ok := rq.(*http.Request)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("got an invalid request:", rq)
|
logg.Warnf("got an invalid request", "req", rq)
|
||||||
return "", handlers.ErrInvalidRequest
|
return "", handlers.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture body (if any) for logging
|
// Capture body (if any) for logging
|
||||||
body, err := io.ReadAll(rqv.Body)
|
body, err := io.ReadAll(rqv.Body)
|
||||||
if err != nil {
|
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)
|
return "", fmt.Errorf("failed to read request body: %v", err)
|
||||||
}
|
}
|
||||||
// Reset the body for further reading
|
// 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)}
|
bodyLog := map[string]string{"body": string(body)}
|
||||||
logBytes, err := json.Marshal(bodyLog)
|
logBytes, err := json.Marshal(bodyLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to marshal request body:", err)
|
logg.Warnf("failed to marshal request body", "err", err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Received request:", string(logBytes))
|
logg.Debugf("received request", "bytes", logBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rqv.ParseForm(); err != nil {
|
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)
|
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)
|
formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
|
||||||
if err != nil {
|
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")
|
return "", fmt.Errorf("failed to format number")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,14 +127,12 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator)
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(128),
|
FlagCount: uint32(128),
|
||||||
MenuSeparator: menuSeparator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
menuSeparator = ": "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -68,14 +67,12 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator)
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(128),
|
FlagCount: uint32(128),
|
||||||
MenuSeparator: menuSeparator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
|
@ -26,7 +26,6 @@ import (
|
|||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
menuSeparator = ": "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -56,14 +55,12 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator)
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(128),
|
FlagCount: uint32(128),
|
||||||
MenuSeparator: menuSeparator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
|
15
cmd/main.go
15
cmd/main.go
@ -18,9 +18,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
menuSeparator = ": "
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -47,15 +46,13 @@ func main() {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator)
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
SessionId: sessionId,
|
SessionId: sessionId,
|
||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(128),
|
FlagCount: uint32(128),
|
||||||
MenuSeparator: menuSeparator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceDir := scriptDir
|
resourceDir := scriptDir
|
||||||
|
54
common/db.go
54
common/db.go
@ -7,43 +7,83 @@ import (
|
|||||||
"git.defalsify.org/vise.git/logging"
|
"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
|
type DataTyp uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DATA_ACCOUNT DataTyp = iota
|
// API Tracking id to follow status of account creation
|
||||||
DATA_ACCOUNT_CREATED
|
DATA_TRACKING_ID = iota
|
||||||
DATA_TRACKING_ID
|
// EVM address returned from API on account creation
|
||||||
DATA_PUBLIC_KEY
|
DATA_PUBLIC_KEY
|
||||||
DATA_CUSTODIAL_ID
|
// Currently active PIN used to authenticate ussd state change requests
|
||||||
DATA_ACCOUNT_PIN
|
DATA_ACCOUNT_PIN
|
||||||
DATA_ACCOUNT_STATUS
|
// The first name of the user
|
||||||
DATA_FIRST_NAME
|
DATA_FIRST_NAME
|
||||||
|
// The last name of the user
|
||||||
DATA_FAMILY_NAME
|
DATA_FAMILY_NAME
|
||||||
|
// The year-of-birth of the user
|
||||||
DATA_YOB
|
DATA_YOB
|
||||||
|
// The location of the user
|
||||||
DATA_LOCATION
|
DATA_LOCATION
|
||||||
|
// The gender of the user
|
||||||
DATA_GENDER
|
DATA_GENDER
|
||||||
|
// The offerings description of the user
|
||||||
DATA_OFFERINGS
|
DATA_OFFERINGS
|
||||||
|
// The ethereum address of the recipient of an ongoing send request
|
||||||
DATA_RECIPIENT
|
DATA_RECIPIENT
|
||||||
|
// The voucher value amount of an ongoing send request
|
||||||
DATA_AMOUNT
|
DATA_AMOUNT
|
||||||
|
// A general swap field for temporary values
|
||||||
DATA_TEMPORARY_VALUE
|
DATA_TEMPORARY_VALUE
|
||||||
|
// Currently active voucher symbol of user
|
||||||
DATA_ACTIVE_SYM
|
DATA_ACTIVE_SYM
|
||||||
|
// Voucher balance of user's currently active voucher
|
||||||
DATA_ACTIVE_BAL
|
DATA_ACTIVE_BAL
|
||||||
|
// String boolean indicating whether use of PIN is blocked
|
||||||
DATA_BLOCKED_NUMBER
|
DATA_BLOCKED_NUMBER
|
||||||
|
// Reverse mapping of a user's evm address to a session id.
|
||||||
DATA_PUBLIC_KEY_REVERSE
|
DATA_PUBLIC_KEY_REVERSE
|
||||||
|
// Decimal count of the currently active voucher
|
||||||
DATA_ACTIVE_DECIMAL
|
DATA_ACTIVE_DECIMAL
|
||||||
|
// EVM address of the currently active voucher
|
||||||
DATA_ACTIVE_ADDRESS
|
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
|
DATA_VOUCHER_SYMBOLS DataTyp = 256 + iota
|
||||||
|
// List of voucher balances for vouchers valid in the user context.
|
||||||
DATA_VOUCHER_BALANCES
|
DATA_VOUCHER_BALANCES
|
||||||
|
// List of voucher decimal counts for vouchers valid in the user context.
|
||||||
DATA_VOUCHER_DECIMALS
|
DATA_VOUCHER_DECIMALS
|
||||||
|
// List of voucher EVM addresses for vouchers valid in the user context.
|
||||||
DATA_VOUCHER_ADDRESSES
|
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
|
DATA_TX_RECIPIENTS
|
||||||
|
// List of voucher values for valid transactions in the user context.
|
||||||
DATA_TX_VALUES
|
DATA_TX_VALUES
|
||||||
|
// List of voucher EVM addresses for valid transactions in the user context.
|
||||||
DATA_TX_ADDRESSES
|
DATA_TX_ADDRESSES
|
||||||
|
// List of valid transaction hashes in the user context.
|
||||||
DATA_TX_HASHES
|
DATA_TX_HASHES
|
||||||
|
// List of transaction dates for valid transactions in the user context.
|
||||||
DATA_TX_DATES
|
DATA_TX_DATES
|
||||||
|
// List of voucher symbols for valid transactions in the user context.
|
||||||
DATA_TX_SYMBOLS
|
DATA_TX_SYMBOLS
|
||||||
|
// List of voucher decimal counts for valid transactions in the user context.
|
||||||
DATA_TX_DECIMALS
|
DATA_TX_DECIMALS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,18 +84,18 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string,
|
|||||||
|
|
||||||
// Adjust for 0-based indexing
|
// Adjust for 0-based indexing
|
||||||
i := index - 1
|
i := index - 1
|
||||||
transactionType := "Received"
|
transactionType := "received"
|
||||||
party := fmt.Sprintf("From: %s", strings.TrimSpace(senders[i]))
|
party := fmt.Sprintf("from: %s", strings.TrimSpace(senders[i]))
|
||||||
if strings.TrimSpace(senders[i]) == publicKey {
|
if strings.TrimSpace(senders[i]) == publicKey {
|
||||||
transactionType = "Sent"
|
transactionType = "sent"
|
||||||
party = fmt.Sprintf("To: %s", strings.TrimSpace(recipients[i]))
|
party = fmt.Sprintf("to: %s", strings.TrimSpace(recipients[i]))
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedDate := formatDate(strings.TrimSpace(dates[i]))
|
formattedDate := formatDate(strings.TrimSpace(dates[i]))
|
||||||
|
|
||||||
// Build the full transaction detail
|
// Build the full transaction detail
|
||||||
detail := fmt.Sprintf(
|
detail := fmt.Sprintf(
|
||||||
"%s %s %s\n%s\nContract address: %s\nTxhash: %s\nDate: %s",
|
"%s %s %s\n%s\ncontract address: %s\ntxhash: %s\ndate: %s",
|
||||||
transactionType,
|
transactionType,
|
||||||
strings.TrimSpace(values[i]),
|
strings.TrimSpace(values[i]),
|
||||||
strings.TrimSpace(syms[i]),
|
strings.TrimSpace(syms[i]),
|
||||||
|
@ -20,7 +20,7 @@ type UserDataStore struct {
|
|||||||
func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) {
|
func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) {
|
||||||
store.SetPrefix(db.DATATYPE_USERDATA)
|
store.SetPrefix(db.DATATYPE_USERDATA)
|
||||||
store.SetSession(sessionId)
|
store.SetSession(sessionId)
|
||||||
k := PackKey(typ, []byte(sessionId))
|
k := ToBytes(typ)
|
||||||
return store.Get(ctx, k)
|
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 {
|
func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error {
|
||||||
store.SetPrefix(db.DATATYPE_USERDATA)
|
store.SetPrefix(db.DATATYPE_USERDATA)
|
||||||
store.SetSession(sessionId)
|
store.SetSession(sessionId)
|
||||||
k := PackKey(typ, []byte(sessionId))
|
k := ToBytes(typ)
|
||||||
return store.Put(ctx, k, value)
|
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
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
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/alecthomas/assert/v2 v2.2.2
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/grassrootseconomics/eth-custodial v1.3.0-beta
|
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.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw=
|
||||||
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.20241212145627-683015d4df80/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=
|
|
||||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
|
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/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
|
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
|
||||||
|
@ -216,7 +216,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte)
|
|||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
store := h.userdataStore
|
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 err != nil {
|
||||||
if db.IsNotFound(err) {
|
if db.IsNotFound(err) {
|
||||||
logg.InfoCtxf(ctx, "Creating an account because it doesn't exist")
|
logg.InfoCtxf(ctx, "Creating an account because it doesn't exist")
|
||||||
@ -1676,11 +1676,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
|
|||||||
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
|
||||||
menuSeparator, ok := ctx.Value("MenuSeparator").(string)
|
|
||||||
if !ok {
|
|
||||||
return res, fmt.Errorf("missing menu Separator")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read vouchers from the store
|
// Read vouchers from the store
|
||||||
voucherData, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS))
|
voucherData, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1688,9 +1683,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte)
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedData := strings.ReplaceAll(string(voucherData), ":", menuSeparator)
|
res.Content = string(voucherData)
|
||||||
|
|
||||||
res.Content = string(formattedData)
|
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@ -1853,19 +1846,13 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionsList reads the list of transactions from the db and formats them
|
// GetTransactionsList fetches the list of transactions and formats them
|
||||||
func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
|
|
||||||
menuSeparator, ok := ctx.Value("MenuSeparator").(string)
|
|
||||||
if !ok {
|
|
||||||
return res, fmt.Errorf("missing menu Separator")
|
|
||||||
}
|
|
||||||
|
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
|
publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1908,12 +1895,12 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []
|
|||||||
value := strings.TrimSpace(values[i])
|
value := strings.TrimSpace(values[i])
|
||||||
date := strings.Split(strings.TrimSpace(dates[i]), " ")[0]
|
date := strings.Split(strings.TrimSpace(dates[i]), " ")[0]
|
||||||
|
|
||||||
status := "Received"
|
status := "received"
|
||||||
if sender == string(publicKey) {
|
if sender == string(publicKey) {
|
||||||
status = "Sent"
|
status = "sent"
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d%s%s %s %s %s", i+1, menuSeparator, status, value, sym, date))
|
formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d:%s %s %s %s", i+1, status, value, sym, date))
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Content = strings.Join(formattedTransactions, "\n")
|
res.Content = strings.Join(formattedTransactions, "\n")
|
||||||
|
@ -1982,11 +1982,7 @@ func TestCheckVouchers(t *testing.T) {
|
|||||||
|
|
||||||
func TestGetVoucherList(t *testing.T) {
|
func TestGetVoucherList(t *testing.T) {
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
menuSeparator := ":"
|
|
||||||
|
|
||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator)
|
|
||||||
|
|
||||||
|
|
||||||
spdb := InitializeTestSubPrefixDb(t, ctx)
|
spdb := InitializeTestSubPrefixDb(t, ctx)
|
||||||
|
|
||||||
|
@ -115,7 +115,8 @@ func(tdb *ThreadGdbmDb) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) {
|
func(tdb *ThreadGdbmDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) {
|
||||||
logg.Warnf("method not implemented for thread gdbm db")
|
tdb.reserve()
|
||||||
return nil, nil
|
defer tdb.release()
|
||||||
|
return tdb.db.Dump(ctx, key)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user