Merge "veyron/services/wspr/wsprd: bind to localhost"
diff --git a/examples/bank/bank.vdl b/examples/bank/bank.vdl
new file mode 100644
index 0000000..bbefdad
--- /dev/null
+++ b/examples/bank/bank.vdl
@@ -0,0 +1,28 @@
+/*
+Package bank implements an application to manipulate virtual money. The client's
+Veyron Identity determines the account they can manipulate. New identity's make
+a new account. Clients can deposit, withdraw, transfer, or query their balance
+in virtual currency.
+*/
+package bank
+
+import "veyron2/security"
+
+// Bank allows clients to store virtual money. Certain implementations can use persistent storage.
+// Uses the client's Veyron Identity to determine account access.
+type Bank interface {
+ // Connect causes the bank to bless a new user (string) and return their bank account number (int64). Existing users are not blessed (""), but still receive their account number (int64).
+ Connect() (newIdentity string, accountNumber int64, err error) {security.WriteLabel}
+}
+
+// The BankAccount can only be accessed by blessed users
+type BankAccount interface {
+ // Deposit adds the amount given to this account.
+ Deposit(amount int64) error {security.WriteLabel}
+ // Withdraw reduces the amount given from this account.
+ Withdraw(amount int64) error {security.WriteLabel}
+ // Transfer moves the amount given to the receiver's account.
+ Transfer(receiver int64, amount int64) error {security.WriteLabel}
+ // Balance returns the amount stored in this account.
+ Balance() (int64, error) {security.ReadLabel}
+}
diff --git a/examples/bank/bank.vdl.go b/examples/bank/bank.vdl.go
new file mode 100644
index 0000000..207d354
--- /dev/null
+++ b/examples/bank/bank.vdl.go
@@ -0,0 +1,446 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: bank.vdl
+
+/*
+Package bank implements an application to manipulate virtual money. The client's
+Veyron Identity determines the account they can manipulate. New identity's make
+a new account. Clients can deposit, withdraw, transfer, or query their balance
+in virtual currency.
+*/
+package bank
+
+import (
+ "veyron2/security"
+
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_veyron2 "veyron2"
+ _gen_context "veyron2/context"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_rt "veyron2/rt"
+ _gen_vdl "veyron2/vdl"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// Bank allows clients to store virtual money. Certain implementations can use persistent storage.
+// Uses the client's Veyron Identity to determine account access.
+// Bank is the interface the client binds and uses.
+// Bank_ExcludingUniversal is the interface without internal framework-added methods
+// to enable embedding without method collisions. Not to be used directly by clients.
+type Bank_ExcludingUniversal interface {
+ // Connect causes the bank to bless a new user (string) and return their bank account number (int64). Existing users are not blessed (""), but still receive their account number (int64).
+ Connect(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (newIdentity string, accountNumber int64, err error)
+}
+type Bank interface {
+ _gen_ipc.UniversalServiceMethods
+ Bank_ExcludingUniversal
+}
+
+// BankService is the interface the server implements.
+type BankService interface {
+
+ // Connect causes the bank to bless a new user (string) and return their bank account number (int64). Existing users are not blessed (""), but still receive their account number (int64).
+ Connect(context _gen_ipc.ServerContext) (newIdentity string, accountNumber int64, err error)
+}
+
+// BindBank returns the client stub implementing the Bank
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindBank(name string, opts ..._gen_ipc.BindOpt) (Bank, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_veyron2.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_vdl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_vdl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubBank{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerBank creates a new server stub.
+//
+// It takes a regular server implementing the BankService
+// interface, and returns a new server stub.
+func NewServerBank(server BankService) interface{} {
+ return &ServerStubBank{
+ service: server,
+ }
+}
+
+// clientStubBank implements Bank.
+type clientStubBank struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (__gen_c *clientStubBank) Connect(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (newIdentity string, accountNumber int64, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Connect", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&newIdentity, &accountNumber, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBank) UnresolveStep(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply []string, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBank) Signature(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply _gen_ipc.ServiceSignature, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Signature", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBank) GetMethodTags(ctx _gen_context.T, method string, opts ..._gen_ipc.CallOpt) (reply []interface{}, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "GetMethodTags", []interface{}{method}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubBank wraps a server that implements
+// BankService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubBank struct {
+ service BankService
+}
+
+func (__gen_s *ServerStubBank) GetMethodTags(call _gen_ipc.ServerCall, method string) ([]interface{}, error) {
+ // TODO(bprosnitz) GetMethodTags() will be replaces with Signature().
+ // Note: This exhibits some weird behavior like returning a nil error if the method isn't found.
+ // This will change when it is replaced with Signature().
+ switch method {
+ case "Connect":
+ return []interface{}{security.Label(2)}, nil
+ default:
+ return nil, nil
+ }
+}
+
+func (__gen_s *ServerStubBank) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Connect"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "newIdentity", Type: 3},
+ {Name: "accountNumber", Type: 37},
+ {Name: "err", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_vdl.Any{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (__gen_s *ServerStubBank) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := __gen_s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubBank) Connect(call _gen_ipc.ServerCall) (newIdentity string, accountNumber int64, err error) {
+ newIdentity, accountNumber, err = __gen_s.service.Connect(call)
+ return
+}
+
+// The BankAccount can only be accessed by blessed users
+// BankAccount is the interface the client binds and uses.
+// BankAccount_ExcludingUniversal is the interface without internal framework-added methods
+// to enable embedding without method collisions. Not to be used directly by clients.
+type BankAccount_ExcludingUniversal interface {
+ // Deposit adds the amount given to this account.
+ Deposit(ctx _gen_context.T, amount int64, opts ..._gen_ipc.CallOpt) (err error)
+ // Withdraw reduces the amount given from this account.
+ Withdraw(ctx _gen_context.T, amount int64, opts ..._gen_ipc.CallOpt) (err error)
+ // Transfer moves the amount given to the receiver's account.
+ Transfer(ctx _gen_context.T, receiver int64, amount int64, opts ..._gen_ipc.CallOpt) (err error)
+ // Balance returns the amount stored in this account.
+ Balance(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply int64, err error)
+}
+type BankAccount interface {
+ _gen_ipc.UniversalServiceMethods
+ BankAccount_ExcludingUniversal
+}
+
+// BankAccountService is the interface the server implements.
+type BankAccountService interface {
+
+ // Deposit adds the amount given to this account.
+ Deposit(context _gen_ipc.ServerContext, amount int64) (err error)
+ // Withdraw reduces the amount given from this account.
+ Withdraw(context _gen_ipc.ServerContext, amount int64) (err error)
+ // Transfer moves the amount given to the receiver's account.
+ Transfer(context _gen_ipc.ServerContext, receiver int64, amount int64) (err error)
+ // Balance returns the amount stored in this account.
+ Balance(context _gen_ipc.ServerContext) (reply int64, err error)
+}
+
+// BindBankAccount returns the client stub implementing the BankAccount
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindBankAccount(name string, opts ..._gen_ipc.BindOpt) (BankAccount, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ client = _gen_rt.R().Client()
+ case 1:
+ switch o := opts[0].(type) {
+ case _gen_veyron2.Runtime:
+ client = o.Client()
+ case _gen_ipc.Client:
+ client = o
+ default:
+ return nil, _gen_vdl.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_vdl.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubBankAccount{client: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerBankAccount creates a new server stub.
+//
+// It takes a regular server implementing the BankAccountService
+// interface, and returns a new server stub.
+func NewServerBankAccount(server BankAccountService) interface{} {
+ return &ServerStubBankAccount{
+ service: server,
+ }
+}
+
+// clientStubBankAccount implements BankAccount.
+type clientStubBankAccount struct {
+ client _gen_ipc.Client
+ name string
+}
+
+func (__gen_c *clientStubBankAccount) Deposit(ctx _gen_context.T, amount int64, opts ..._gen_ipc.CallOpt) (err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Deposit", []interface{}{amount}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) Withdraw(ctx _gen_context.T, amount int64, opts ..._gen_ipc.CallOpt) (err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Withdraw", []interface{}{amount}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) Transfer(ctx _gen_context.T, receiver int64, amount int64, opts ..._gen_ipc.CallOpt) (err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Transfer", []interface{}{receiver, amount}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) Balance(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply int64, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Balance", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) UnresolveStep(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply []string, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) Signature(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply _gen_ipc.ServiceSignature, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "Signature", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubBankAccount) GetMethodTags(ctx _gen_context.T, method string, opts ..._gen_ipc.CallOpt) (reply []interface{}, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client.StartCall(ctx, __gen_c.name, "GetMethodTags", []interface{}{method}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubBankAccount wraps a server that implements
+// BankAccountService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubBankAccount struct {
+ service BankAccountService
+}
+
+func (__gen_s *ServerStubBankAccount) GetMethodTags(call _gen_ipc.ServerCall, method string) ([]interface{}, error) {
+ // TODO(bprosnitz) GetMethodTags() will be replaces with Signature().
+ // Note: This exhibits some weird behavior like returning a nil error if the method isn't found.
+ // This will change when it is replaced with Signature().
+ switch method {
+ case "Deposit":
+ return []interface{}{security.Label(2)}, nil
+ case "Withdraw":
+ return []interface{}{security.Label(2)}, nil
+ case "Transfer":
+ return []interface{}{security.Label(2)}, nil
+ case "Balance":
+ return []interface{}{security.Label(1)}, nil
+ default:
+ return nil, nil
+ }
+}
+
+func (__gen_s *ServerStubBankAccount) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Balance"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 37},
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["Deposit"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "amount", Type: 37},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["Transfer"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "receiver", Type: 37},
+ {Name: "amount", Type: 37},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+ result.Methods["Withdraw"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "amount", Type: 37},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_vdl.Any{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (__gen_s *ServerStubBankAccount) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := __gen_s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubBankAccount) Deposit(call _gen_ipc.ServerCall, amount int64) (err error) {
+ err = __gen_s.service.Deposit(call, amount)
+ return
+}
+
+func (__gen_s *ServerStubBankAccount) Withdraw(call _gen_ipc.ServerCall, amount int64) (err error) {
+ err = __gen_s.service.Withdraw(call, amount)
+ return
+}
+
+func (__gen_s *ServerStubBankAccount) Transfer(call _gen_ipc.ServerCall, receiver int64, amount int64) (err error) {
+ err = __gen_s.service.Transfer(call, receiver, amount)
+ return
+}
+
+func (__gen_s *ServerStubBankAccount) Balance(call _gen_ipc.ServerCall) (reply int64, err error) {
+ reply, err = __gen_s.service.Balance(call)
+ return
+}
diff --git a/examples/bank/bank/main.go b/examples/bank/bank/main.go
new file mode 100644
index 0000000..92228f6
--- /dev/null
+++ b/examples/bank/bank/main.go
@@ -0,0 +1,204 @@
+// 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"
+ 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 = security.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(derivedIdentity))
+ 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])
+ }
+ }
+}
diff --git a/examples/bank/pbankd/main.go b/examples/bank/pbankd/main.go
new file mode 100644
index 0000000..9e27a8b
--- /dev/null
+++ b/examples/bank/pbankd/main.go
@@ -0,0 +1,439 @@
+// Binary pbankd is a simple implementation of the bank service.
+// Binary bank clients can connect to this bank service to manage virtual bank
+// accounts. Unlike bankd, pbankd uses the Veyron Store. It can recover user
+// data after a crash, but it must always connect to the stored service.
+// Unlike bankd, pbankd prevents race conditions with transactions.
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "math/rand"
+ "os"
+ "os/user"
+ "path/filepath"
+ "reflect"
+ "regexp"
+ "strings"
+ "time"
+
+ "veyron/examples/bank"
+ "veyron/examples/bank/schema"
+ "veyron/lib/signals"
+ "veyron/security/caveat"
+ idutil "veyron/services/identity/util"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/storage"
+ "veyron2/storage/vstore"
+ "veyron2/storage/vstore/primitives"
+ "veyron2/vlog"
+)
+
+// Duration of a bank account blessing, intended to be very long.
+const BLESS_DURATION = 24 * 10000 * time.Hour
+
+// Ensure that account numbers are all 6 digits long.
+const MIN_ACCOUNT_NUMBER = 100000
+const MAX_ACCOUNT_NUMBER = 999999
+const SUFFIX_REGEXP = "/[0-9]{6}"
+
+// Where we will store the bank in the store database.
+const BANK_ROOT string = "/Bank"
+
+var (
+ storeName string
+ ACCOUNTS string
+ runtime veyron2.Runtime
+)
+
+func init() {
+ username := "unknown"
+ if u, err := user.Current(); err == nil {
+ username = u.Username
+ }
+ hostname := "unknown"
+ if h, err := os.Hostname(); err == nil {
+ hostname = h
+ }
+ dir := "global/vstore/" + hostname + "/" + username
+
+ // Parse the flags (variableName, flagName, defaultValue, description)
+ flag.StringVar(&storeName, "store", dir, "Name of the Veyron store")
+
+ // Set the random seed to the current time to increase psuedorandomness.
+ rand.Seed(time.Now().Unix())
+
+ // Set the name for the ACCOUNTS 'constant'. TODO(alexfandrianto): Is there a better way to know the field is named "Accounts"?
+ ACCOUNTS = reflect.TypeOf(schema.Bank{}).Field(0).Name
+}
+
+// The following struct and functions handle construction of the persistent bank.
+type pbankd struct {
+ // Pointer to the store
+ store storage.Store
+
+ // Current Transaction
+ transaction storage.Transaction
+
+ // The bank's private ID (used for blessing)
+ ID security.PrivateID
+}
+
+// newPbankd creates a new persistent bank structure.
+func newPbankd(store storage.Store, identity security.PrivateID) *pbankd {
+ b := &pbankd{
+ store: store,
+ ID: identity,
+ }
+ return b
+}
+
+// InitializeBank bank details in the store; currently only initializes the root.
+func (b *pbankd) initializeBank() {
+ b.newTransaction()
+ b.initializeBankRoot()
+ if err := b.commit(); err != nil {
+ vlog.Fatal(err)
+ }
+}
+
+// initializeBankRoot prepares the bank root as BANK_ROOT if it isn't yet in the Veyron store.
+func (b *pbankd) initializeBankRoot() {
+ // Create parent directories for the bank root, if necessary
+ l := strings.Split(BANK_ROOT, "/")
+ fmt.Println(l)
+ for i, _ := range l {
+ fmt.Println(i)
+ prefix := filepath.Join(l[:i]...)
+ o := b.store.Bind(prefix)
+ if exist, err := o.Exists(runtime.TODOContext(), b.transaction); err != nil {
+ vlog.Infof("Error checking existence at %q: %s", prefix, err)
+ } else if !exist {
+ if _, err := o.Put(runtime.TODOContext(), b.transaction, &schema.Dir{}); err != nil {
+ vlog.Infof("Error creating parent %q: %s", prefix, err)
+ }
+ fmt.Printf("%q was created!\n", prefix)
+ } else {
+ fmt.Printf("%q was already present in the store.\n", prefix)
+ }
+ }
+
+ // Add the bank schema to the store at BANK_ROOT, if necessary
+ o := b.store.Bind(BANK_ROOT)
+ if exist, err := o.Exists(runtime.TODOContext(), b.transaction); err != nil {
+ vlog.Infof("Error checking existence at %q: %s", BANK_ROOT, err)
+ } else if !exist {
+ _, err := o.Put(runtime.TODOContext(), b.transaction, &schema.Bank{})
+ if err != nil {
+ vlog.Infof("Error creating bank at %q: %s", BANK_ROOT, err)
+ }
+ }
+}
+
+// Register creates an account for a new user with the given bankName.
+func (b *pbankd) Connect(context ipc.ServerContext) (string, int64, error) {
+ // Check if the RemoteID() has been blessed by the bank
+ if num := getBankAccountNumber(context); num != 0 {
+ // Look up the user and return their bank account number
+ fmt.Println("This client is blessed!")
+ fmt.Printf("ID: %d\n", num)
+ return "", num, nil
+ } else {
+ fmt.Println("This client isn't blessed. Let's bless them!")
+ // Use the store
+ b.newTransaction()
+
+ // Keep rolling until we get an unseen number
+ randID := rand.Int63n(MAX_ACCOUNT_NUMBER-MIN_ACCOUNT_NUMBER) + MIN_ACCOUNT_NUMBER
+ for b.isUser(randID) {
+ randID = rand.Int63n(MAX_ACCOUNT_NUMBER-MIN_ACCOUNT_NUMBER) + MIN_ACCOUNT_NUMBER
+ }
+ fmt.Printf("ID: %d\n", randID)
+
+ // Bless the user
+ pp := security.PrincipalPattern(context.LocalID().Names()[0])
+ pID, err := b.ID.Bless(
+ context.RemoteID(),
+ fmt.Sprintf("%d", randID),
+ BLESS_DURATION,
+ []security.ServiceCaveat{security.UniversalCaveat(caveat.PeerIdentity{pp})},
+ )
+ if err != nil {
+ vlog.Fatal(err)
+ }
+
+ // Encode the public ID
+ enc, err := idutil.Base64VomEncode(pID)
+ if err != nil {
+ vlog.Fatal(err)
+ }
+
+ // Store the user into the database
+ b.registerNewUser(randID)
+ err = b.commit()
+ if err != nil {
+ vlog.Fatal(err)
+ }
+
+ // Ensure the new user is a user before returning the blessing and ID
+ if b.isUser(randID) {
+ return enc, randID, nil
+ }
+ return "", 0, fmt.Errorf("failed to register user")
+ }
+}
+
+// Deposit adds the amount given to this account.
+func (b *pbankd) Deposit(context ipc.ServerContext, amount int64) error {
+ user := getBankAccountNumber(context)
+ if user == 0 {
+ return fmt.Errorf("couldn't retrieve account number")
+ }
+ b.newTransaction()
+ if !b.isUser(user) {
+ return fmt.Errorf("user isn't registered")
+ } else if amount < 0 {
+ return fmt.Errorf("deposit amount %d is negative", amount)
+ }
+ b.changeBalance(user, amount)
+ return b.commit()
+}
+
+// Withdraw reduces the amount given from this account.
+func (b *pbankd) Withdraw(context ipc.ServerContext, amount int64) error {
+ user := getBankAccountNumber(context)
+ if user == 0 {
+ return fmt.Errorf("couldn't retrieve account number")
+ }
+ b.newTransaction()
+ if !b.isUser(user) {
+ return fmt.Errorf("user isn't registered")
+ } else if amount < 0 {
+ return fmt.Errorf("withdraw amount %d is negative", amount)
+ } else if balance := b.checkBalance(user); amount > balance {
+ return fmt.Errorf("withdraw amount %d exceeds balance %d", amount, balance)
+ }
+ b.changeBalance(user, -amount)
+ return b.commit()
+}
+
+// Transfer moves the amount given to the receiver.
+func (b *pbankd) Transfer(context ipc.ServerContext, accountNumber int64, amount int64) error {
+ user := getBankAccountNumber(context)
+ if user == 0 {
+ return fmt.Errorf("couldn't retrieve account number")
+ }
+ b.newTransaction()
+ if !b.isUser(user) {
+ return fmt.Errorf("user isn't registered")
+ } else if !b.isUser(accountNumber) {
+ return fmt.Errorf("%d isn't registered", accountNumber)
+ } else if amount < 0 {
+ return fmt.Errorf("transfer amount %d is negative", amount)
+ } else if balance := b.checkBalance(user); amount > balance {
+ return fmt.Errorf("transfer amount %d exceeds balance %d", amount, balance)
+ }
+ b.changeBalance(user, -amount)
+ b.changeBalance(accountNumber, amount)
+ return b.commit()
+}
+
+// Balance returns the amount stored by the given user.
+// Throws an error if the user is invalid.
+func (b *pbankd) Balance(context ipc.ServerContext) (int64, error) {
+ user := getBankAccountNumber(context)
+ if user == 0 {
+ return 0, fmt.Errorf("couldn't retrieve account number")
+ }
+ if !b.isUser(user) {
+ return 0, fmt.Errorf("user isn't registered")
+ }
+ return b.checkBalance(user), nil
+}
+
+/*
+ Helper functions for the persistent bank service that deal with store access.
+*/
+
+// newTransaction starts a new transaction.
+func (b *pbankd) newTransaction() {
+ b.transaction = primitives.NewTransaction(runtime.TODOContext())
+}
+
+// commit commits the current transaction.
+func (b *pbankd) commit() error {
+ err := b.transaction.Commit(runtime.TODOContext())
+ b.transaction = nil
+ if err != nil {
+ return fmt.Errorf("Failed to commit transaction: %s", err)
+ }
+ return nil
+}
+
+// isUser helps determine if the given user is part of the system or not.
+func (b *pbankd) isUser(accountNumber int64) bool {
+ // If this is a user, their location in the store should exist.
+ prefix := filepath.Join(BANK_ROOT, ACCOUNTS, fmt.Sprintf("%d", accountNumber))
+ o := b.store.Bind(prefix)
+ exist, err := o.Exists(runtime.TODOContext(), b.transaction)
+ if err != nil {
+ vlog.Infof("Error checking existence at %s: %s", prefix, err)
+ return false
+ }
+ return exist
+}
+
+// Obtains the bank account number of the user. Returns 0 if it could not be found.
+func getBankAccountNumber(context ipc.ServerContext) int64 {
+ bankName := context.LocalID().Names()[0]
+
+ // Untrusted clients have no names.
+ if len(context.RemoteID().Names()) == 0 {
+ return 0
+ }
+
+ // Otherwise, extract the account number from the name.
+ name := context.RemoteID().Names()[0]
+ if match, err := regexp.MatchString(bankName+SUFFIX_REGEXP, name); err != nil {
+ vlog.Infof("MatchString error: %s", err)
+ return 0
+ } else if !match {
+ vlog.Infof("No matching name found")
+ return 0
+ }
+ var v int64
+ _, err := fmt.Sscanf(name, bankName+"/%d", &v)
+ if err != nil {
+ vlog.Infof("Failure to parse ID from %s: %s", name, err)
+ return 0
+ }
+ return v
+}
+
+// registerNewUser adds the user to the system under the specified name and returns success.
+func (b *pbankd) registerNewUser(user int64) {
+ // Create the user's account
+ prefix := filepath.Join(BANK_ROOT, ACCOUNTS, fmt.Sprintf("%d", user))
+ o := b.store.Bind(prefix)
+ if _, err := o.Put(runtime.TODOContext(), b.transaction, int64(0)); err != nil {
+ vlog.Infof("Error creating %s: %s", prefix, err)
+ }
+}
+
+// checkBalance gets the user's balance from the store
+func (b *pbankd) checkBalance(user int64) int64 {
+ prefix := filepath.Join(BANK_ROOT, ACCOUNTS, fmt.Sprintf("%d", user))
+ o := b.store.Bind(prefix)
+ e, err := o.Get(runtime.TODOContext(), b.transaction)
+ if err != nil {
+ vlog.Infof("Error getting %s: %s", prefix, err)
+ }
+ value, _ := e.Value.(int64)
+ return value
+}
+
+// changeBalance modifies the user's balance in the store
+func (b *pbankd) changeBalance(user int64, amount int64) {
+ prefix := filepath.Join(BANK_ROOT, ACCOUNTS, fmt.Sprintf("%d", user))
+ o := b.store.Bind(prefix)
+ e, err := o.Get(runtime.TODOContext(), b.transaction)
+ if err != nil {
+ vlog.Infof("Error getting %s: %s", prefix, err)
+ }
+ if _, err := o.Put(runtime.TODOContext(), b.transaction, e.Value.(int64)+amount); err != nil {
+ vlog.Infof("Error changing %s: %s", prefix, err)
+ }
+}
+
+// This custom bank dispatcher has two interfaces with distinct authorizers.
+func newBankDispatcher(bankServer interface{}, bankAccountServer interface{}, authBank security.Authorizer, authBankAccount security.Authorizer) ipc.Dispatcher {
+ return BankDispatcher{ipc.ReflectInvoker(bankServer), ipc.ReflectInvoker(bankAccountServer), authBank, authBankAccount}
+}
+
+type BankDispatcher struct {
+ invokerBank ipc.Invoker
+ invokerBankAccount ipc.Invoker
+ authBank security.Authorizer
+ authBankAccount security.Authorizer
+}
+
+func (d BankDispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ fmt.Println("Dispatcher Lookup Suffix:", suffix)
+ if suffix != "" {
+ return d.invokerBankAccount, d.authBankAccount, nil
+ }
+ return d.invokerBank, d.authBank, nil
+}
+
+// The custom account authorizer checks if the RemoteID matches a regexp pattern and allows only Reads and Writes.
+type AccountAuthorizer string
+
+func (aa AccountAuthorizer) Authorize(ctx security.Context) error {
+ name := ctx.RemoteID().Names()[0]
+ match, err := regexp.MatchString(string(aa), name)
+ fmt.Printf("Authorizing for Account %s %t\n", name, match)
+ if err != nil {
+ return err
+ }
+ if match {
+ if ctx.Label() != security.ReadLabel && ctx.Label() != security.WriteLabel {
+ return errors.New("unauthorized; may only read or write")
+ }
+ return nil
+ }
+ return errors.New("unauthorized to access account")
+}
+
+func main() {
+ // Create a new server instance.
+ runtime = rt.Init()
+ s, err := runtime.NewServer()
+ if err != nil {
+ vlog.Fatal("failure creating server: ", err)
+ }
+
+ // Connect to the Veyron Store
+ vlog.Infof("Binding to store on %s", storeName)
+ st, err := vstore.New(storeName)
+ if err != nil {
+ vlog.Fatalf("Can't connect to store: %s: %s", storeName, err)
+ }
+
+ // Create the bank server and bank account server stubs, using the store connection
+ pbankd := newPbankd(st, runtime.Identity())
+ pbankd.initializeBank()
+ bankServer := bank.NewServerBank(pbankd)
+ bankAccountServer := bank.NewServerBankAccount(pbankd)
+
+ // Setup bank and account authorizers.
+ bankAuth := security.NewACLAuthorizer(security.ACL{security.AllPrincipals: security.LabelSet(security.ReadLabel | security.WriteLabel)})
+ bankAccountAuth := AccountAuthorizer(runtime.Identity().PublicID().Names()[0] + SUFFIX_REGEXP)
+
+ // Register the "bank" prefix with a bank dispatcher.
+ if err := s.Register("bank", newBankDispatcher(bankServer, bankAccountServer, bankAuth, bankAccountAuth)); err != nil {
+ vlog.Fatal("error registering service: ", err)
+ }
+
+ // Create an endpoint and begin listening.
+ endpoint, err := s.Listen("tcp", "127.0.0.1:0")
+ if err == nil {
+ fmt.Printf("Listening at: %v\n", endpoint)
+ } else {
+ vlog.Fatal("error listening to service: ", err)
+ }
+
+ // Publish the service in the mount table.
+ mountName := "veyron"
+ fmt.Printf("Mounting bank on %s, endpoint /%s\n", mountName, endpoint)
+ if err := s.Publish(mountName); err != nil {
+ vlog.Fatal("s.Publish() failed: ", err)
+ }
+
+ // Wait forever.
+ <-signals.ShutdownOnSignals()
+}
diff --git a/examples/bank/schema/init.go b/examples/bank/schema/init.go
new file mode 100644
index 0000000..2040e0f
--- /dev/null
+++ b/examples/bank/schema/init.go
@@ -0,0 +1,11 @@
+// This schema registers structs used in the bank example to the VOM for the Veyron store.
+package schema
+
+import (
+ "veyron2/vom"
+)
+
+func init() {
+ vom.Register(&Dir{})
+ vom.Register(&Bank{})
+}
diff --git a/examples/bank/schema/schema.vdl b/examples/bank/schema/schema.vdl
new file mode 100644
index 0000000..0a34090
--- /dev/null
+++ b/examples/bank/schema/schema.vdl
@@ -0,0 +1,13 @@
+package schema
+
+// Dir is used to represent directories.
+type Dir struct{
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// Bank is used to represent the information stored in a bank.
+type Bank struct {
+ Accounts map[string]int64
+}
diff --git a/examples/bank/schema/schema.vdl.go b/examples/bank/schema/schema.vdl.go
new file mode 100644
index 0000000..8d51fc8
--- /dev/null
+++ b/examples/bank/schema/schema.vdl.go
@@ -0,0 +1,16 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: schema.vdl
+
+package schema
+
+// Dir is used to represent directories.
+type Dir struct {
+ // TODO(jyh): The IDL does not recognize empty structs. Fix it and remove this
+ // useless field.
+ X byte
+}
+
+// Bank is used to represent the information stored in a bank.
+type Bank struct {
+ Accounts map[string]int64
+}
diff --git a/services/store/typeregistryhack/init.go b/services/store/typeregistryhack/init.go
index 31a37f1..2b5c0ac 100644
--- a/services/store/typeregistryhack/init.go
+++ b/services/store/typeregistryhack/init.go
@@ -14,6 +14,8 @@
_ "veyron/examples/storage/mdb/schema"
// Register todos types.
_ "veyron/examples/todos/schema"
+ // Register bank types.
+ _ "veyron/examples/bank/schema"
"veyron2/vom"
)