blob: efbcce9d5015da4878d34f77d4258c10e532db04 [file] [log] [blame]
// Binary bank is a bank client that interacts with bankd and pbankd.
// Users connect to a bank service and can send commands to it.
package main
import (
"bufio"
"flag"
"fmt"
"os"
"strconv"
"strings"
"veyron/examples/bank"
vsecurity "veyron/security"
idutil "veyron/services/identity/util"
"veyron2"
"veyron2/naming"
"veyron2/rt"
"veyron2/security"
)
var (
// TODO(rthellend): Remove the address flag when the config manager is working.
address = flag.String("address", "", "the address/endpoint of the bank server")
serverPattern = flag.String("server_pattern", "*", "server_pattern is an optional pattern for the expected identity of the fortune server. Example: the pattern \"myorg/fortune\" matches identities with names \"myorg/fortune\" or \"myorg\". If the flag is absent then the default pattern \"*\" matches all identities.")
accountMountTableName string
)
const bankMountTableName string = "veyron/bank"
// Parses str and returns a nonnegative integer
func getInt(str string, name string) (int64, bool) {
v, err := strconv.ParseInt(str, 0, 64)
if err != nil {
fmt.Printf("given %s %s was not an integer: %s\n", name, str, err)
return 0, true
}
return v, false
}
func main() {
// Create the Veyron runtime using VEYRON_IDENTITY
runtime := rt.Init()
log := runtime.Logger()
// Construct a new stub that binds to serverEndpoint without
// using the name service
var bankServer bank.Bank
var err error
if *address != "" {
bankServer, err = bank.BindBank(naming.JoinAddressName(*address, "//bank"))
} else {
bankServer, err = bank.BindBank("veyron/bank")
}
if err != nil {
log.Fatal("error binding to server: ", err)
}
// First, Connect to the bank to retrieve the location you should bind to.
newIdentity, bankAccountNumber, err := bankServer.Connect(runtime.TODOContext(), veyron2.RemoteID(*serverPattern))
if err != nil {
log.Fatalf("%s.Connect failed: %s\n", bankMountTableName, err)
}
fmt.Printf("Successfully connected to the bank with account: %d\n", bankAccountNumber)
client := runtime.Client()
// If you got a new identity, you should update your PrivateID and make a new client with that identity.
if newIdentity != "" {
// Decode the new identity
var newPublicID security.PublicID
err := idutil.Base64VomDecode(newIdentity, &newPublicID)
if err != nil {
log.Fatalf("failed to decode the given public id: %s\n", err)
}
// Derive the new PrivateID
derivedIdentity, err := runtime.Identity().Derive(newPublicID)
if err != nil {
log.Fatalf("failed to derive identity: %s\n", err)
}
// Update the PrivateID at VEYRON_IDENTITY
path := os.Getenv("VEYRON_IDENTITY")
f, err := os.OpenFile(path, os.O_WRONLY, 0600)
if err != nil {
log.Fatalf("failed to open identity file for writing: %s\n", err)
}
err = vsecurity.SaveIdentity(f, derivedIdentity)
if err != nil {
log.Fatalf("failed to save identity: %s\n", err)
}
err = f.Close()
if err != nil {
log.Fatalf("could not close file %q, %s\n", path, err)
}
// Make a NewClient and update the other one. Do not close the old client; we need its connection to the Mount Table.
client, err = runtime.NewClient(veyron2.LocalID(newPublicID))
if err != nil {
log.Fatalf("failed to create new client: %s\n", err)
}
}
// Bind to the bank account service
accountMountTableName = fmt.Sprintf("veyron/bank/%d", bankAccountNumber)
bindLocation := fmt.Sprintf("//bank/%d", bankAccountNumber)
var accountServer bank.BankAccount
if *address != "" { // Connect directly to the server's endpoint
accountServer, err = bank.BindBankAccount(naming.JoinAddressName(*address, bindLocation), client)
} else { // Use the Mount Table to connect to the server
accountServer, err = bank.BindBankAccount(accountMountTableName, client)
}
if err != nil {
log.Fatalf("error binding to server: ", err)
}
// Read commands from standard input until the user decides to quit/exit.
end := false
for !end {
fmt.Print("Your command: ")
// Read input from user
in := bufio.NewReader(os.Stdin)
input, _ := in.ReadString('\n')
commands := strings.Fields(input)
if len(commands) == 0 {
fmt.Println("Please enter a command. 'help' displays the list of commands.")
continue
}
switch commands[0] {
case "bankAccountNumber":
fmt.Printf("Your bank account number is %d\n", bankAccountNumber)
case "deposit":
if len(commands) < 2 {
fmt.Println("deposit takes an amount")
break
}
amount, e := getInt(commands[1], "amount")
if e {
break
}
err := accountServer.Deposit(runtime.TODOContext(), amount, veyron2.RemoteID(*serverPattern))
if err != nil {
fmt.Printf("%s.Deposit failed: %s\n", accountMountTableName, err)
break
}
fmt.Printf("Deposited %d\n", amount)
case "withdraw":
if len(commands) < 2 {
fmt.Println("withdraw takes an amount")
break
}
amount, e := getInt(commands[1], "amount")
if e {
break
}
err := accountServer.Withdraw(runtime.TODOContext(), amount, veyron2.RemoteID(*serverPattern))
if err != nil {
fmt.Printf("%s.Withdraw failed: %s\n", accountMountTableName, err)
break
}
fmt.Printf("Withdrew %d\n", amount)
case "transfer":
if len(commands) < 3 {
fmt.Println("transfer takes an account number and an amount")
break
}
accountNumber, e1 := getInt(commands[1], "account number")
amount, e2 := getInt(commands[2], "amount")
if e1 || e2 {
break
}
err := accountServer.Transfer(runtime.TODOContext(), accountNumber, amount, veyron2.RemoteID(*serverPattern))
if err != nil {
fmt.Printf("%s.Transfer failed: %s\n", accountMountTableName, err)
break
}
fmt.Printf("Transferred %d to %d\n", amount, accountNumber)
case "balance":
balance, err := accountServer.Balance(runtime.TODOContext(), veyron2.RemoteID(*serverPattern))
if err != nil {
fmt.Printf("%s.Balance failed: %s\n", accountMountTableName, err)
break
}
fmt.Printf("User has balance %d\n", balance)
case "help":
fmt.Println("You can do the following actions:")
fmt.Println("bankAccountNumber: check your account number")
fmt.Println("balance: check your account balance")
fmt.Println("deposit [N>=0]: deposit money into your account")
fmt.Println("withdraw [N>=0]: withdraw money from your account")
fmt.Println("transfer [account number] [N>=0]: transfer money to another account")
fmt.Println("quit: quit the program")
fmt.Println("exit: also quits the program")
case "exit", "quit":
fmt.Println("Quitting...")
end = true
default:
fmt.Printf("Unknown command: %s\n", commands[0])
}
}
}