Compare commits

..

No commits in common. "c6c66f956a23e051f131fe17ff1b137b105e144d" and "ef0c207fc4efa84de51198ba3db976231cdd194a" have entirely different histories.

22 changed files with 380 additions and 576 deletions

View File

@ -2,14 +2,10 @@ package main
import ( import (
"context" "context"
"encoding/csv"
"flag" "flag"
"fmt" "fmt"
"io"
"log"
"os" "os"
"path" "path"
"strconv"
"git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/cache"
"git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/engine"
@ -17,6 +13,7 @@ import (
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/state" "git.defalsify.org/vise.git/state"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd" "git.grassecon.net/urdt/ussd/internal/handlers/ussd"
"git.grassecon.net/urdt/ussd/internal/models"
) )
var ( var (
@ -38,43 +35,22 @@ func main() {
ctx := context.Background() ctx := context.Background()
st := state.NewState(16) st := state.NewState(16)
st.UseDebug() st.UseDebug()
state.FlagDebugger.Register(models.USERFLAG_LANGUAGE_SET, "LANGUAGE_CHANGE")
pfp := path.Join(scriptDir, "pp.csv") state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATED, "ACCOUNT_CREATED")
file, err := os.Open(pfp) state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_SUCCESS, "ACCOUNT_SUCCESS")
if err != nil { state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_PENDING, "ACCOUNT_PENDING")
fmt.Fprintf(os.Stderr, "Failed to open CSV file: %v\n", err) state.FlagDebugger.Register(models.USERFLAG_INCORRECTPIN, "INCORRECTPIN")
os.Exit(1) state.FlagDebugger.Register(models.USERFLAG_INCORRECTDATEFORMAT, "INVALIDDATEFORMAT")
} state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT, "INVALIDRECIPIENT")
defer file.Close() state.FlagDebugger.Register(models.USERFLAG_PINMISMATCH, "PINMISMATCH")
reader := csv.NewReader(file) state.FlagDebugger.Register(models.USERFLAG_PIN_SET, "PIN_SET")
state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE, "INVALIDRECIPIENT_WITH_INVITE")
// Iterate through the CSV records and register the flags state.FlagDebugger.Register(models.USERFLAG_INVALID_AMOUNT, "INVALIDAMOUNT")
for { state.FlagDebugger.Register(models.USERFLAG_ALLOW_UPDATE, "UNLOCKFORUPDATE")
record, err := reader.Read() state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "VALIDPIN")
if err != nil { state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "ACCOUNTUNLOCKED")
if err == io.EOF { state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATION_FAILED, "ACCOUNT_CREATION_FAILED")
break state.FlagDebugger.Register(models.USERFLAG_SINGLE_EDIT, "SINGLEEDIT")
}
fmt.Fprintf(os.Stderr, "Error reading CSV file: %v\n", err)
os.Exit(1)
}
// Ensure the record starts with "flag" and has at least 3 columns
if len(record) < 3 || record[0] != "flag" {
continue
}
flagName := record[1]
flagValue, err := strconv.Atoi(record[2])
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to convert flag value %s to integer: %v\n", record[2], err)
continue
}
// Register the flag
log.Printf("Registering flagName:%s; flagValue:%v", flagName, flagValue)
state.FlagDebugger.Register(uint32(flagValue), flagName)
}
rfs := resource.NewFsResource(scriptDir) rfs := resource.NewFsResource(scriptDir)
ca := cache.NewCache() ca := cache.NewCache()
@ -84,7 +60,7 @@ func main() {
} }
dp := path.Join(scriptDir, ".state") dp := path.Join(scriptDir, ".state")
err = os.MkdirAll(dp, 0700) err := os.MkdirAll(dp, 0700)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "state dir create exited with error: %v\n", err) fmt.Fprintf(os.Stderr, "state dir create exited with error: %v\n", err)
os.Exit(1) os.Exit(1)
@ -107,11 +83,7 @@ func main() {
fp := path.Join(dp, sessionId) fp := path.Join(dp, sessionId)
ussdHandlers, err := ussd.NewHandlers(fp, &st, sessionId) ussdHandlers := ussd.NewHandlers(fp, &st)
if err != nil {
fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err)
}
rfs.AddLocalFunc("select_language", ussdHandlers.SetLanguage) rfs.AddLocalFunc("select_language", ussdHandlers.SetLanguage)
rfs.AddLocalFunc("create_account", ussdHandlers.CreateAccount) rfs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@ package ussd
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -21,15 +20,6 @@ type MockAccountService struct {
mock.Mock mock.Mock
} }
type MockFlagParser struct {
mock.Mock
}
func (m *MockFlagParser) GetFlag(key string) (uint32, error) {
args := m.Called(key)
return args.Get(0).(uint32), args.Error(1)
}
func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) {
args := m.Called() args := m.Called()
return args.Get(0).(*models.AccountResponse), args.Error(1) return args.Get(0).(*models.AccountResponse), args.Error(1)
@ -79,20 +69,11 @@ func TestCreateAccount(t *testing.T) {
// Set up expectations for the mock account service // Set up expectations for the mock account service
mockAccountService.On("CreateAccount").Return(mockAccountResponse, nil) mockAccountService.On("CreateAccount").Return(mockAccountResponse, nil)
mockParser := new(MockFlagParser)
flag_account_created := uint32(1)
flag_account_creation_failed := uint32(2)
mockParser.On("GetFlag", "flag_account_created").Return(flag_account_created, nil)
mockParser.On("GetFlag", "flag_account_creation_failed").Return(flag_account_creation_failed, nil)
// Initialize Handlers with mock account service // Initialize Handlers with mock account service
h := &Handlers{ h := &Handlers{
fs: &FSData{Path: accountFilePath}, fs: &FSData{Path: accountFilePath},
accountFileHandler: accountFileHandler, accountFileHandler: accountFileHandler,
accountService: mockAccountService, accountService: mockAccountService,
parser: mockParser,
} }
tests := []struct { tests := []struct {
@ -105,7 +86,7 @@ func TestCreateAccount(t *testing.T) {
name: "New account creation", name: "New account creation",
existingData: nil, existingData: nil,
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagSet: []uint32{flag_account_created}, FlagSet: []uint32{models.USERFLAG_ACCOUNT_CREATED},
}, },
expectedData: map[string]string{ expectedData: map[string]string{
"TrackingId": "test-tracking-id", "TrackingId": "test-tracking-id",
@ -266,16 +247,10 @@ func TestSavePin(t *testing.T) {
// Create a new AccountFileHandler and set it in the Handlers struct // Create a new AccountFileHandler and set it in the Handlers struct
accountFileHandler := utils.NewAccountFileHandler(accountFilePath) accountFileHandler := utils.NewAccountFileHandler(accountFilePath)
mockParser := new(MockFlagParser)
h := &Handlers{ h := &Handlers{
accountFileHandler: accountFileHandler, accountFileHandler: accountFileHandler,
parser: mockParser,
} }
flag_incorrect_pin := uint32(1)
mockParser.On("GetFlag", "flag_incorrect_pin").Return(flag_incorrect_pin, nil)
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
@ -296,21 +271,21 @@ func TestSavePin(t *testing.T) {
{ {
name: "Invalid PIN - non-numeric", name: "Invalid PIN - non-numeric",
input: []byte("12ab"), input: []byte("12ab"),
expectedFlags: []uint32{flag_incorrect_pin}, expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN},
expectedData: initialAccountData, // No changes expected expectedData: initialAccountData, // No changes expected
expectedErrors: false, expectedErrors: false,
}, },
{ {
name: "Invalid PIN - less than 4 digits", name: "Invalid PIN - less than 4 digits",
input: []byte("123"), input: []byte("123"),
expectedFlags: []uint32{flag_incorrect_pin}, expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN},
expectedData: initialAccountData, // No changes expected expectedData: initialAccountData, // No changes expected
expectedErrors: false, expectedErrors: false,
}, },
{ {
name: "Invalid PIN - more than 4 digits", name: "Invalid PIN - more than 4 digits",
input: []byte("12345"), input: []byte("12345"),
expectedFlags: []uint32{flag_incorrect_pin}, expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN},
expectedData: initialAccountData, // No changes expected expectedData: initialAccountData, // No changes expected
expectedErrors: false, expectedErrors: false,
}, },
@ -318,6 +293,7 @@ func TestSavePin(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Ensure the file exists before running the test
err := accountFileHandler.EnsureFileExists() err := accountFileHandler.EnsureFileExists()
if err != nil { if err != nil {
t.Fatalf("Failed to ensure account file exists: %v", err) t.Fatalf("Failed to ensure account file exists: %v", err)
@ -714,6 +690,7 @@ func TestSaveOfferings(t *testing.T) {
} }
} }
func TestSaveGender(t *testing.T) { func TestSaveGender(t *testing.T) {
// Create a new instance of MockAccountFileHandler // Create a new instance of MockAccountFileHandler
mockFileHandler := new(mocks.MockAccountFileHandler) mockFileHandler := new(mocks.MockAccountFileHandler)
@ -775,7 +752,7 @@ func TestSaveGender(t *testing.T) {
mockFileHandler.On("WriteAccountData", mock.MatchedBy(func(data map[string]string) bool { mockFileHandler.On("WriteAccountData", mock.MatchedBy(func(data map[string]string) bool {
return data["Gender"] == tt.expectedGender return data["Gender"] == tt.expectedGender
})).Return(tt.writeError) })).Return(tt.writeError)
} else if len(tt.input) == 0 { } else if len(tt.input) == 0 {
// For empty input, no WriteAccountData call should be made // For empty input, no WriteAccountData call should be made
mockFileHandler.On("WriteAccountData", mock.Anything).Maybe().Return(tt.writeError) mockFileHandler.On("WriteAccountData", mock.Anything).Maybe().Return(tt.writeError)
} }
@ -899,95 +876,3 @@ func TestGetAmount(t *testing.T) {
} }
} }
func TestGetProfileInfo(t *testing.T) {
tests := []struct {
name string
accountData map[string]string
readError error
expectedResult resource.Result
expectedError error
}{
{
name: "Complete Profile",
accountData: map[string]string{
"FirstName": "John",
"FamilyName": "Doe",
"Gender": "Male",
"YOB": "1980",
"Location": "Mombasa",
"Offerings": "Product A",
},
readError: nil,
expectedResult: resource.Result{
Content: fmt.Sprintf(
"Name: %s %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n",
"John", "Doe", "Male", 44, "Mombasa", "Product A",
),
},
expectedError: nil,
},
{
name: "Profile with Not Provided Fields",
accountData: map[string]string{
"FirstName": "Not provided",
"FamilyName": "Doe",
"Gender": "Female",
"YOB": "1995",
"Location": "Not provided",
"Offerings": "Service B",
},
readError: nil,
expectedResult: resource.Result{
Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n",
"Not provided", "Female", 29, "Not provided", "Service B",
),
},
expectedError: nil,
},
{
name: "Profile with YOB as Not provided",
accountData: map[string]string{
"FirstName": "Not provided",
"FamilyName": "Doe",
"Gender": "Female",
"YOB": "Not provided",
"Location": "Not provided",
"Offerings": "Service B",
},
readError: nil,
expectedResult: resource.Result{
Content: fmt.Sprintf(
"Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n",
"Not provided", "Female", "Not provided", "Not provided", "Service B",
),
},
expectedError: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a new instance of MockAccountFileHandler
mockFileHandler := new(mocks.MockAccountFileHandler)
// Set up the mock expectations
mockFileHandler.On("ReadAccountData").Return(tt.accountData, tt.readError)
// Create the Handlers instance with the mock file handler
h := &Handlers{
accountFileHandler: mockFileHandler,
}
// Call the method
result, err := h.GetProfileInfo(context.Background(), "get_profile_info", nil)
// Assert the results
assert.Equal(t, tt.expectedResult, result)
assert.Equal(t, tt.expectedError, err)
// Assert all expectations were met
mockFileHandler.AssertExpectations(t)
})
}
}

22
internal/models/flags.go Normal file
View File

@ -0,0 +1,22 @@
package models
import "git.defalsify.org/vise.git/state"
const (
USERFLAG_LANGUAGE_SET = iota + state.FLAG_USERSTART
USERFLAG_ACCOUNT_CREATED
USERFLAG_ACCOUNT_PENDING
USERFLAG_ACCOUNT_SUCCESS
USERFLAG_ACCOUNT_AUTHORIZED
USERFLAG_INVALID_RECIPIENT
USERFLAG_INVALID_RECIPIENT_WITH_INVITE
USERFLAG_INCORRECTPIN
USERFLAG_ALLOW_UPDATE
USERFLAG_INVALID_AMOUNT
USERFLAG_PIN_SET
USERFLAG_VALIDPIN
USERFLAG_PINMISMATCH
USERFLAG_INCORRECTDATEFORMAT
USERFLAG_ACCOUNT_CREATION_FAILED
USERFLAG_SINGLE_EDIT
)

View File

@ -4,7 +4,7 @@ TXTS = $(wildcard ./*.txt.orig)
# Rule to build .bin files from .vis files # Rule to build .bin files from .vis files
%.vis: %.vis:
go run ../../go-vise/dev/asm -f pp.csv $(basename $@).vis > $(basename $@).bin go run ../../go-vise/dev/asm $(basename $@).vis > $(basename $@).bin
@echo "Built $(basename $@).bin from $(basename $@).vis" @echo "Built $(basename $@).bin from $(basename $@).vis"
# Rule to copy .orig files to .txt # Rule to copy .orig files to .txt

View File

@ -1,4 +1,4 @@
RELOAD verify_pin RELOAD verify_pin
CATCH create_pin_mismatch flag_pin_mismatch 1 CATCH create_pin_mismatch 20 1
LOAD quit 0 LOAD quit 0
HALT HALT

View File

@ -1,3 +1,3 @@
RELOAD check_account_status RELOAD check_account_status
CATCH main flag_account_success 1 CATCH main 11 1
HALT HALT

View File

@ -5,7 +5,7 @@ MOUT back 0
HALT HALT
LOAD validate_amount 64 LOAD validate_amount 64
RELOAD validate_amount RELOAD validate_amount
CATCH invalid_amount flag_invalid_amount 1 CATCH invalid_amount 17 1
INCMP _ 0 INCMP _ 0
LOAD get_recipient 12 LOAD get_recipient 12
LOAD get_sender 64 LOAD get_sender 64

View File

@ -1,4 +1,4 @@
LOAD reset_account_authorized 0 LOAD reset_unlocked 0
MOUT my_balance 1 MOUT my_balance 1
MOUT community_balance 2 MOUT community_balance 2
MOUT back 0 MOUT back 0

View File

@ -1,5 +1,5 @@
LOAD reset_incorrect 0 LOAD reset_incorrect 0
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin 15 1
CATCH pin_entry flag_account_authorized 0 CATCH pin_entry 12 0
LOAD quit_with_balance 0 LOAD quit_with_balance 0
HALT HALT

View File

@ -1,9 +1,9 @@
LOAD create_account 0 LOAD create_account 0
CATCH account_creation_failed flag_account_creation_failed 1 CATCH account_creation_failed 22 1
MOUT exit 0 MOUT exit 0
HALT HALT
LOAD save_pin 0 LOAD save_pin 0
RELOAD save_pin RELOAD save_pin
CATCH . flag_incorrect_pin 1 CATCH . 15 1
INCMP quit 0 INCMP quit 0
INCMP confirm_create_pin * INCMP confirm_create_pin *

View File

@ -1,9 +1,12 @@
CATCH incorrect_date_format flag_incorrect_date_format 1 CATCH incorrect_date_format 21 1
LOAD save_yob 0 LOAD save_yob 0
CATCH update_success flag_allow_update 1 CATCH update_success 16 1
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
LOAD save_location 0 LOAD save_location 0
CATCH pin_entry flag_single_edit 1 CATCH pin_entry 23 1
INCMP enter_offerings * INCMP enter_offerings *

View File

@ -1,6 +1,6 @@
LOAD save_location 0 LOAD save_location 0
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin 15 1
CATCH update_success flag_allow_update 1 CATCH update_success 16 1
MOUT back 0 MOUT back 0
HALT HALT
LOAD save_offerings 0 LOAD save_offerings 0

View File

@ -1,9 +1,9 @@
LOAD save_gender 0 LOAD save_gender 0
CATCH update_success flag_allow_update 1 CATCH update_success 16 1
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0
LOAD verify_yob 8 LOAD verify_yob 8
LOAD save_yob 0 LOAD save_yob 0
CATCH pin_entry flag_single_edit 1 CATCH pin_entry 23 1
INCMP enter_location * INCMP enter_location *

View File

@ -1,5 +1,5 @@
LOAD reset_incorrect 0 LOAD reset_incorrect 0
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin 15 1
CATCH pin_entry flag_account_authorized 0 CATCH pin_entry 12 0
LOAD quit_with_balance 0 LOAD quit_with_balance 0
HALT HALT

View File

@ -1,16 +0,0 @@
flag,flag_language_set,8,checks whether the user has set their prefered language
flag,flag_account_created,9,this is set when an account has been created on the API
flag,flag_account_creation_failed,10,this is set when there's an error from the API during account creation
flag,flag_account_pending,11,this is set when an account does not have a status of SUCCESS
flag,flag_account_success,12,this is set when an account has a status of SUCCESS
flag,flag_pin_mismatch,13,this is set when the confirmation PIN matches the initial PIN during registration
flag,flag_pin_set,14,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu
flag,flag_account_authorized,15,this is set to allow a user access guarded nodes after providing a correct PIN
flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid
flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform
flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid
flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN
flag,flag_valid_pin,20,this is set when the given PIN is valid
flag,flag_allow_update,21,this is set to allow a user to update their profile data
flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth
flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid
1 flag flag_language_set 8 checks whether the user has set their prefered language
2 flag flag_account_created 9 this is set when an account has been created on the API
3 flag flag_account_creation_failed 10 this is set when there's an error from the API during account creation
4 flag flag_account_pending 11 this is set when an account does not have a status of SUCCESS
5 flag flag_account_success 12 this is set when an account has a status of SUCCESS
6 flag flag_pin_mismatch 13 this is set when the confirmation PIN matches the initial PIN during registration
7 flag flag_pin_set 14 this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu
8 flag flag_account_authorized 15 this is set to allow a user access guarded nodes after providing a correct PIN
9 flag flag_invalid_recipient 16 this is set when the transaction recipient is invalid
10 flag flag_invalid_recipient_with_invite 17 this is set when the transaction recipient is valid but not on the platform
11 flag flag_invalid_amount 18 this is set when the given transaction amount is invalid
12 flag flag_incorrect_pin 19 this is set when the provided PIN is invalid or does not match the current account's PIN
13 flag flag_valid_pin 20 this is set when the given PIN is valid
14 flag flag_allow_update 21 this is set to allow a user to update their profile data
15 flag flag_single_edit 22 this is set to allow a user to edit a single profile item such as year of birth
16 flag flag_incorrect_date_format 23 this is set when the given year of birth is invalid

View File

@ -1,7 +1,7 @@
CATCH select_language flag_language_set 0 CATCH select_language 8 0
CATCH terms flag_account_created 0 CATCH terms 9 0
LOAD check_account_status 0 LOAD check_account_status 0
CATCH account_pending flag_account_pending 1 CATCH account_pending 10 1
CATCH create_pin flag_pin_set 0 CATCH create_pin 18 0
CATCH main flag_account_success 1 CATCH main 11 1
HALT HALT

View File

@ -1,12 +1,12 @@
LOAD save_familyname 0 LOAD save_familyname 0
CATCH update_success flag_allow_update 1 CATCH update_success 16 1
MOUT male 1 MOUT male 1
MOUT female 2 MOUT female 2
MOUT unspecified 3 MOUT unspecified 3
MOUT back 0 MOUT back 0
HALT HALT
LOAD save_gender 0 LOAD save_gender 0
CATCH pin_entry flag_single_edit 1 CATCH pin_entry 23 1
INCMP _ 0 INCMP _ 0
INCMP enter_yob 1 INCMP enter_yob 1
INCMP enter_yob 2 INCMP enter_yob 2

View File

@ -3,6 +3,6 @@ MOUT back 0
HALT HALT
LOAD validate_recipient 20 LOAD validate_recipient 20
RELOAD validate_recipient RELOAD validate_recipient
CATCH invalid_recipient flag_invalid_recipient 1 CATCH invalid_recipient 13 1
INCMP _ 0 INCMP _ 0
INCMP amount * INCMP amount *

View File

@ -1,6 +1,4 @@
LOAD reset_incorrect 6 LOAD reset_incorrect 0
CATCH incorrect_pin flag_incorrect_pin 1
CATCH _ flag_account_authorized 0
LOAD get_amount 10 LOAD get_amount 10
MAP get_amount MAP get_amount
RELOAD get_recipient RELOAD get_recipient

View File

@ -6,9 +6,10 @@ MAP get_sender
MOUT back 0 MOUT back 0
MOUT quit 9 MOUT quit 9
HALT HALT
LOAD authorize_account 6 LOAD authorize_account 1
RELOAD authorize_account RELOAD authorize_account
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin 15 1
INCMP _ 0 INCMP _ 0
INCMP quit 9 INCMP quit 9
INCMP transaction_initiated * MOVE transaction_initiated

View File

@ -1,8 +1,8 @@
LOAD get_profile_info 0 LOAD get_profile_info 0
MAP get_profile_info MAP get_profile_info
LOAD reset_incorrect 6 LOAD reset_incorrect 0
CATCH incorrect_pin flag_incorrect_pin 1 CATCH incorrect_pin 15 1
CATCH pin_entry flag_account_authorized 0 CATCH pin_entry 12 0
MOUT back 0 MOUT back 0
HALT HALT
INCMP _ 0 INCMP _ 0