veyron/examples/bank

Create the bank example to illustrate storage and security.

For illustration purposes, we store a bank schema that includes a map field. Transactions and the store ensure data validity and enable persistent storage for the service.

The bank server detects whether a client is new or already has an account using an ACLAuthorizer. New clients are blessed and overwrite their old identity. Clients (new and old) then connect to the server under a different suffix. The dispatcher then allows them to interact with the BankAccountServer and make changes to their account in the store.

Interestingly, we must keep the old client open after this rename, since the mount table connection uses the old client's identity.

We mount the server onto the mount table so that the client can connect to a fixed bank location (instead of an endpoint). The same is true between the server and the store. All programs must run with the MOUNTTABLE_ROOT environment variable set.

To run, we need a mount table and store service running as well as several identities (like myorg and bankC)
1: VEYRON_IDENTITY=root ./go/bin/mounttabled --address=:5167     [take port 5167]
2: VEYRON_IDENTITY=myorg MOUNTTABLE_ROOT=/localhost:5167/mt ./go/bin/stored
3: Start the bank server.
   VEYRON_IDENTITY=myorg MOUNTTABLE_ROOT=/localhost:5167/mt ./go/bin/pbankd
4: Start the bank client.
   VEYRON_IDENTITY=bankC MOUNTTABLE_ROOT=/localhost:5167/mt ./go/bin/bank
5: Start another bank client.
   VEYRON_IDENTITY=bankD ./go/bin/bank

The above assumes root has blessed myorg. bankC and bankD are any other identity.

The clients will automatically connect and register themselves with the Bank server. Then they can interact with their accounts (deposit, withdraw, transfer, balance, etc.)

Change-Id: Ifaa72d7bfefc869588eef9fd57d4ef1162dce7dc
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"
 )