Merge "rt: Reparent the agents context to the root."
diff --git a/lib/security/blessingroots.go b/lib/security/blessingroots.go
index 43417b2..e5748bf 100644
--- a/lib/security/blessingroots.go
+++ b/lib/security/blessingroots.go
@@ -12,6 +12,7 @@
 
 	"v.io/v23/security"
 	"v.io/v23/verror"
+	"v.io/x/lib/vlog"
 	"v.io/x/ref/lib/security/serialization"
 )
 
@@ -51,6 +52,7 @@
 		br.state[key] = patterns[:len(patterns)-1]
 		return err
 	}
+
 	return nil
 }
 
@@ -70,6 +72,23 @@
 	return security.NewErrUnrecognizedRoot(nil, root.String(), nil)
 }
 
+func (br *blessingRoots) Dump() map[security.BlessingPattern][]security.PublicKey {
+	dump := make(map[security.BlessingPattern][]security.PublicKey)
+	br.mu.RLock()
+	defer br.mu.RUnlock()
+	for keyStr, patterns := range br.state {
+		key, err := security.UnmarshalPublicKey([]byte(keyStr))
+		if err != nil {
+			vlog.Errorf("security.UnmarshalPublicKey(%v) returned error: %v", []byte(keyStr), err)
+			return nil
+		}
+		for _, p := range patterns {
+			dump[p] = append(dump[p], key)
+		}
+	}
+	return dump
+}
+
 // DebugString return a human-readable string encoding of the roots
 // DebugString encodes all roots into a string in the following
 // format
diff --git a/lib/security/blessingroots_test.go b/lib/security/blessingroots_test.go
index e7ff446..a8ef94f 100644
--- a/lib/security/blessingroots_test.go
+++ b/lib/security/blessingroots_test.go
@@ -8,13 +8,15 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"reflect"
+	"sort"
 	"testing"
 
 	"v.io/v23/security"
 	"v.io/v23/verror"
 )
 
-type rootsTester [3]security.PublicKey
+type rootsTester [4]security.PublicKey
 
 func newRootsTester() *rootsTester {
 	var tester rootsTester
@@ -34,6 +36,7 @@
 	}{
 		{t[0], "vanadium"},
 		{t[1], "google/foo"},
+		{t[2], "google/foo"},
 		{t[0], "google/$"},
 	}
 	for _, d := range testdata {
@@ -62,6 +65,11 @@
 		},
 		{
 			root:          t[2],
+			recognized:    []string{"google/foo", "google/foo/bar"},
+			notRecognized: []string{"google", "google/bar", "vanadium", "vanadium/foo", "foo", "foo/bar"},
+		},
+		{
+			root:          t[3],
 			recognized:    []string{},
 			notRecognized: []string{"vanadium", "vanadium/foo", "vanadium/bar", "google", "google/foo", "google/bar", "foo", "foo/bar"},
 		},
@@ -74,13 +82,34 @@
 		}
 		for _, b := range d.notRecognized {
 			if err, want := br.Recognized(d.root, b), security.ErrUnrecognizedRoot.ID; verror.ErrorID(err) != want {
-				return fmt.Errorf("Recognized(%v, %q): Got %v(errorid=%v), want errorid=%v", d.root, b, err, verror.ErrorID(err), want)
+				return fmt.Errorf("Recognized(%v, %q): got %v(errorid=%v), want errorid=%v", d.root, b, err, verror.ErrorID(err), want)
 			}
 		}
 	}
 	return nil
 }
 
+type pubKeySorter []security.PublicKey
+
+func (s pubKeySorter) Len() int           { return len(s) }
+func (s pubKeySorter) Less(i, j int) bool { return s[i].String() < s[j].String() }
+func (s pubKeySorter) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+
+func (t *rootsTester) testDump(br security.BlessingRoots) error {
+	want := map[security.BlessingPattern][]security.PublicKey{
+		"google/foo": []security.PublicKey{t[1], t[2]},
+		"google/$":   []security.PublicKey{t[0]},
+		"vanadium":   []security.PublicKey{t[0]},
+	}
+	got := br.Dump()
+	sort.Sort(pubKeySorter(want["google/foo"]))
+	sort.Sort(pubKeySorter(got["google/foo"]))
+	if !reflect.DeepEqual(got, want) {
+		return fmt.Errorf("Dump(): got %v, want %v", got, want)
+	}
+	return nil
+}
+
 func TestBlessingRoots(t *testing.T) {
 	p, err := NewPrincipal()
 	if err != nil {
@@ -93,6 +122,9 @@
 	if err := tester.testRecognized(p.Roots()); err != nil {
 		t.Fatal(err)
 	}
+	if err := tester.testDump(p.Roots()); err != nil {
+		t.Fatal(err)
+	}
 }
 
 func TestBlessingRootsPersistence(t *testing.T) {
@@ -112,6 +144,9 @@
 	if err := tester.testRecognized(p.Roots()); err != nil {
 		t.Error(err)
 	}
+	if err := tester.testDump(p.Roots()); err != nil {
+		t.Error(err)
+	}
 	// Recreate the principal (and thus BlessingRoots)
 	p2, err := LoadPersistentPrincipal(dir, nil)
 	if err != nil {
@@ -120,4 +155,7 @@
 	if err := tester.testRecognized(p2.Roots()); err != nil {
 		t.Error(err)
 	}
+	if err := tester.testDump(p2.Roots()); err != nil {
+		t.Error(err)
+	}
 }
diff --git a/services/agent/agentlib/agent_test.go b/services/agent/agentlib/agent_test.go
index 9f3eff7..1a9064a 100644
--- a/services/agent/agentlib/agent_test.go
+++ b/services/agent/agentlib/agent_test.go
@@ -25,6 +25,22 @@
 	_ "v.io/x/ref/profiles"
 )
 
+// As of April 28, 2015, the benchmarks for serving a principal with and
+// without the agent are as follows:
+//
+// BenchmarkSignNoAgent                    :  889608 ns/op
+// BenchmarkSignCachedAgent                : 6961410 ns/op
+// BenchmarkSignUncachedAgent	           : 7403763 ns/op
+// BenchmarkDefaultNoAgent	           :     139 ns/op
+// BenchmarkDefaultCachedAgent	           :      41 ns/op
+// BenchmarkDefaultUncachedAgent	   : 9732978 ns/op
+// BenchmarkRecognizedNegativeNoAgent	   :   34859 ns/op
+// BenchmarkRecognizedNegativeCachedAgent  :   31043 ns/op
+// BenchmarkRecognizedNegativeUncachedAgent: 5110308 ns/op
+// BenchmarkRecognizedNoAgent	           :   13457 ns/op
+// BenchmarkRecognizedCachedAgent	   :   12609 ns/op
+// BenchmarkRecognizedUncachedAgent	   : 4232959 ns/op
+
 //go:generate v23 test generate
 
 func getPrincipalAndHang(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
diff --git a/services/agent/agentlib/client.go b/services/agent/agentlib/client.go
index bcda630..7dc2c3c 100644
--- a/services/agent/agentlib/client.go
+++ b/services/agent/agentlib/client.go
@@ -272,6 +272,26 @@
 	return b.caller.call("BlessingRootsRecognized", results(), marshalledKey, blessing)
 }
 
+func (b *blessingRoots) Dump() map[security.BlessingPattern][]security.PublicKey {
+	var marshaledRoots map[security.BlessingPattern][][]byte
+	if err := b.caller.call("BlessingRootsDump", results(&marshaledRoots)); err != nil {
+		vlog.Errorf("error calling BlessingRootsDump: %v", err)
+		return nil
+	}
+	ret := make(map[security.BlessingPattern][]security.PublicKey)
+	for p, marshaledKeys := range marshaledRoots {
+		for _, marshaledKey := range marshaledKeys {
+			key, err := security.UnmarshalPublicKey(marshaledKey)
+			if err != nil {
+				vlog.Errorf("security.UnmarshalPublicKey(%v) returned error: %v", marshaledKey, err)
+				continue
+			}
+			ret[p] = append(ret[p], key)
+		}
+	}
+	return ret
+}
+
 func (b *blessingRoots) DebugString() (s string) {
 	err := b.caller.call("BlessingRootsDebugString", results(&s))
 	if err != nil {
diff --git a/services/agent/internal/cache/cache.go b/services/agent/internal/cache/cache.go
index e16ca7a..252b6a3 100644
--- a/services/agent/internal/cache/cache.go
+++ b/services/agent/internal/cache/cache.go
@@ -5,7 +5,6 @@
 package cache
 
 import (
-	"encoding/base64"
 	"fmt"
 	"sync"
 
@@ -29,26 +28,24 @@
 
 // cachedRoots is a security.BlessingRoots implementation that
 // wraps over another implementation and adds caching.
-// It caches both known roots, and a blessings known to be untrusted.
-// Only the negative cache is cleared when flushing, since BlessingRoots
-// doesn't allow removal.
 type cachedRoots struct {
-	mu       *sync.RWMutex
-	impl     security.BlessingRoots
-	cache    map[string][]security.BlessingPattern
-	negative *lru.Cache // key + blessing -> error
+	mu    *sync.RWMutex
+	impl  security.BlessingRoots
+	cache map[string][]security.BlessingPattern // GUARDED_BY(mu)
+
+	// TODO(ataly): Get rid of the following fields once all agents have been
+	// updated to support the 'Dump' method.
+	dumpExists bool       // GUARDED_BY(my)
+	negative   *lru.Cache // key + blessing -> error
 }
 
-func newCachedRoots(impl security.BlessingRoots, mu *sync.RWMutex) *cachedRoots {
+func newCachedRoots(impl security.BlessingRoots, mu *sync.RWMutex) (*cachedRoots, error) {
 	roots := &cachedRoots{mu: mu, impl: impl}
 	roots.flush()
-	return roots
-}
-
-// Must be called while holding mu.
-func (r *cachedRoots) flush() {
-	r.negative = lru.New(maxNegativeCacheEntries)
-	r.cache = make(map[string][]security.BlessingPattern)
+	if err := roots.fetchAndCacheRoots(); err != nil {
+		return nil, err
+	}
+	return roots, nil
 }
 
 func (r *cachedRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error {
@@ -60,19 +57,15 @@
 	defer r.mu.Unlock()
 	r.mu.Lock()
 
-	err = r.impl.Add(root, pattern)
-	if err == nil {
+	if err := r.impl.Add(root, pattern); err != nil {
+		return err
+	}
+
+	if r.cache != nil {
+
 		r.cache[cacheKey] = append(r.cache[cacheKey], pattern)
 	}
-	return err
-}
-
-func keyToString(key security.PublicKey) (string, error) {
-	bytes, err := key.MarshalBinary()
-	if err != nil {
-		return "", err
-	}
-	return base64.StdEncoding.EncodeToString(bytes), nil
+	return nil
 }
 
 func (r *cachedRoots) Recognized(root security.PublicKey, blessing string) (result error) {
@@ -82,41 +75,144 @@
 	}
 
 	r.mu.RLock()
+	var cacheExists bool
+	if r.cache != nil {
+		err = r.recognizeFromCache(key, root, blessing)
+		cacheExists = true
+	}
+	r.mu.RUnlock()
 
+	if !cacheExists {
+		r.mu.Lock()
+		if err := r.fetchAndCacheRoots(); err != nil {
+			r.mu.Unlock()
+			return err
+		}
+		err = r.recognizeFromCache(key, root, blessing)
+		r.mu.Unlock()
+	}
+
+	// TODO(ataly): Get rid of the following block once all agents have been updated
+	// to support the 'Dump' method.
+	r.mu.RLock()
+	if !r.dumpExists && err != nil {
+		negKey := key + blessing
+		negErr, ok := r.negative.Get(negKey)
+		if !ok {
+			r.mu.RUnlock()
+			return r.recognizeFromImpl(key, root, blessing)
+		}
+		r.negative.Put(negKey, err)
+		err = negErr.(error)
+	}
+	r.mu.RUnlock()
+
+	return err
+}
+
+func (r *cachedRoots) Dump() map[security.BlessingPattern][]security.PublicKey {
+	var (
+		cacheExists bool
+		dump        map[security.BlessingPattern][]security.PublicKey
+	)
+
+	r.mu.RLock()
+	if r.cache != nil {
+		cacheExists = true
+		dump = r.dumpFromCache()
+	}
+	r.mu.RUnlock()
+
+	if !cacheExists {
+		r.mu.Lock()
+		if err := r.fetchAndCacheRoots(); err != nil {
+			vlog.Errorf("failed to cache roots: %v", err)
+			r.mu.Unlock()
+			return nil
+		}
+		dump = r.dumpFromCache()
+		r.mu.Unlock()
+	}
+	return dump
+}
+
+func (r *cachedRoots) DebugString() string {
+	return r.impl.DebugString()
+}
+
+// Must be called while holding mu.
+func (r *cachedRoots) fetchAndCacheRoots() error {
+	dump := r.impl.Dump()
+	r.cache = make(map[string][]security.BlessingPattern)
+	if dump == nil {
+		r.dumpExists = false
+		return nil
+	}
+
+	for p, keys := range dump {
+		for _, key := range keys {
+			cacheKey, err := keyToString(key)
+			if err != nil {
+				return err
+			}
+			r.cache[cacheKey] = append(r.cache[cacheKey], p)
+		}
+	}
+	r.dumpExists = true
+	return nil
+}
+
+// Must be called while holding mu.
+func (r *cachedRoots) flush() {
+	r.cache = nil
+	r.negative = lru.New(maxNegativeCacheEntries)
+}
+
+// Must be called while holding mu.
+func (r *cachedRoots) dumpFromCache() map[security.BlessingPattern][]security.PublicKey {
+	if !r.dumpExists {
+		return nil
+	}
+	dump := make(map[security.BlessingPattern][]security.PublicKey)
+	for keyStr, patterns := range r.cache {
+		key, err := security.UnmarshalPublicKey([]byte(keyStr))
+		if err != nil {
+			vlog.Errorf("security.UnmarshalPublicKey(%v) returned error: %v", []byte(keyStr), err)
+			return nil
+		}
+		for _, p := range patterns {
+			dump[p] = append(dump[p], key)
+		}
+	}
+	return dump
+}
+
+// Must be called while holding mu.
+func (r *cachedRoots) recognizeFromCache(key string, root security.PublicKey, blessing string) error {
 	for _, p := range r.cache[key] {
 		if p.MatchedBy(blessing) {
-			r.mu.RUnlock()
 			return nil
 		}
 	}
-	r.mu.RUnlock()
-	r.mu.Lock()
-	negKey := key + blessing
-	if err, ok := r.negative.Get(negKey); ok {
-		r.negative.Put(negKey, err)
-		r.mu.Unlock()
-		return err.(error)
-	}
-	r.mu.Unlock()
-	return r.recognizeAndCache(key, root, blessing)
+	return security.NewErrUnrecognizedRoot(nil, root.String(), nil)
 }
 
-func (r *cachedRoots) recognizeAndCache(key string, root security.PublicKey, blessing string) error {
+// TODO(ataly): Get rid of this method once all agents have been updated
+// to support the 'Dump' method.
+func (r *cachedRoots) recognizeFromImpl(key string, root security.PublicKey, blessing string) error {
+	negKey := key + blessing
 	err := r.impl.Recognized(root, blessing)
+
 	r.mu.Lock()
 	if err == nil {
 		r.cache[key] = append(r.cache[key], security.BlessingPattern(blessing))
 	} else {
-		r.negative.Put(key+blessing, err)
+		r.negative.Put(negKey, err)
 	}
 	r.mu.Unlock()
 	return err
 }
 
-func (r cachedRoots) DebugString() string {
-	return r.impl.DebugString()
-}
-
 // cachedStore is a security.BlessingStore implementation that
 // wraps over another implementation and adds caching.
 type cachedStore struct {
@@ -216,7 +312,7 @@
 	return fmt.Sprintf("cached[%s]", s.impl)
 }
 
-// Must be called while holding mu
+// Must be called while holding mu.
 func (s *cachedStore) flush() {
 	s.hasDef = false
 	s.peers = nil
@@ -264,7 +360,10 @@
 
 func NewCachedPrincipal(ctx *context.T, impl security.Principal, call rpc.ClientCall) (p security.Principal, err error) {
 	var mu sync.RWMutex
-	cachedRoots := newCachedRoots(impl.Roots(), &mu)
+	cachedRoots, err := newCachedRoots(impl.Roots(), &mu)
+	if err != nil {
+		return
+	}
 	cachedStore := &cachedStore{mu: &mu, key: impl.PublicKey(), impl: impl.BlessingStore()}
 	flush := func() {
 		defer mu.Unlock()
@@ -293,3 +392,11 @@
 	p = &cachedPrincipal{p, impl}
 	return
 }
+
+func keyToString(key security.PublicKey) (string, error) {
+	bytes, err := key.MarshalBinary()
+	if err != nil {
+		return "", err
+	}
+	return string(bytes), nil
+}
diff --git a/services/agent/internal/cache/cache_test.go b/services/agent/internal/cache/cache_test.go
index 852d0ce..cd89a5d0 100644
--- a/services/agent/internal/cache/cache_test.go
+++ b/services/agent/internal/cache/cache_test.go
@@ -17,7 +17,11 @@
 	var mu sync.RWMutex
 	p := testutil.NewPrincipal()
 	impl := p.Roots()
-	return p.PublicKey(), impl, newCachedRoots(impl, &mu)
+	roots, err := newCachedRoots(impl, &mu)
+	if err != nil {
+		panic(err)
+	}
+	return p.PublicKey(), impl, roots
 }
 
 func TestCreateRoots(t *testing.T) {
@@ -33,7 +37,7 @@
 func expectRecognized(roots security.BlessingRoots, key security.PublicKey, blessing string) string {
 	err := roots.Recognized(key, blessing)
 	if err != nil {
-		return fmt.Sprintf("Key (%s, %b) not matched by roots:\n%s", key, blessing, roots.DebugString())
+		return fmt.Sprintf("Key (%s, %b) not matched by roots:\n%s, Recognized returns error: %v", key, blessing, roots.DebugString(), err)
 	}
 	return ""
 }
@@ -129,6 +133,29 @@
 	}
 }
 
+func TestRootsDump(t *testing.T) {
+	key, impl, cache := createRoots()
+
+	if err := cache.Add(key, "alice/friend"); err != nil {
+		t.Fatalf("Add failed: %v", err)
+	}
+
+	orig := impl.Dump()
+	if got := cache.Dump(); !reflect.DeepEqual(orig, got) {
+		t.Errorf("Dump() got %v, want %v", got, orig)
+	}
+
+	impl.Add(key, "carol")
+	if got := cache.Dump(); !reflect.DeepEqual(orig, got) {
+		t.Errorf("Dump() got %v, want %v", got, orig)
+	}
+
+	cache.flush()
+	if cur, got := impl.Dump(), cache.Dump(); !reflect.DeepEqual(cur, got) {
+		t.Errorf("Dump() got %v, want %v", got, cur)
+	}
+}
+
 func createStore(p security.Principal) (security.BlessingStore, *cachedStore) {
 	var mu sync.RWMutex
 	impl := p.BlessingStore()
diff --git a/services/agent/internal/server/server.go b/services/agent/internal/server/server.go
index 32ae171..55e239f 100644
--- a/services/agent/internal/server/server.go
+++ b/services/agent/internal/server/server.go
@@ -435,6 +435,22 @@
 	return a.principal.Roots().Recognized(pkey, blessing)
 }
 
+func (a agentd) BlessingRootsDump(_ *context.T, _ rpc.ServerCall) (map[security.BlessingPattern][][]byte, error) {
+	ret := make(map[security.BlessingPattern][][]byte)
+	a.w.rlock()
+	defer a.w.runlock()
+	for p, keys := range a.principal.Roots().Dump() {
+		for _, key := range keys {
+			marshaledKey, err := key.MarshalBinary()
+			if err != nil {
+				return nil, err
+			}
+			ret[p] = append(ret[p], marshaledKey)
+		}
+	}
+	return ret, nil
+}
+
 func (a agentd) BlessingRootsDebugString(_ *context.T, _ rpc.ServerCall) (string, error) {
 	a.w.rlock()
 	defer a.w.runlock()
diff --git a/services/agent/wire.vdl b/services/agent/wire.vdl
index 4c93acb..994aaf5 100644
--- a/services/agent/wire.vdl
+++ b/services/agent/wire.vdl
@@ -55,6 +55,7 @@
 
 	BlessingRootsAdd(root []byte, pattern security.BlessingPattern) error
 	BlessingRootsRecognized(root []byte, blessing string) error
+	BlessingRootsDump() (map[security.BlessingPattern][][]byte | error)
 	BlessingRootsDebugString() (string | error)
 
 	// Clients using caching should call NotifyWhenChanged upon connecting to
diff --git a/services/agent/wire.vdl.go b/services/agent/wire.vdl.go
index 6b29ef1..1a0e6a2 100644
--- a/services/agent/wire.vdl.go
+++ b/services/agent/wire.vdl.go
@@ -65,6 +65,7 @@
 	BlessingStoreDebugString(*context.T, ...rpc.CallOpt) (string, error)
 	BlessingRootsAdd(ctx *context.T, root []byte, pattern security.BlessingPattern, opts ...rpc.CallOpt) error
 	BlessingRootsRecognized(ctx *context.T, root []byte, blessing string, opts ...rpc.CallOpt) error
+	BlessingRootsDump(*context.T, ...rpc.CallOpt) (map[security.BlessingPattern][][]byte, error)
 	BlessingRootsDebugString(*context.T, ...rpc.CallOpt) (string, error)
 	// Clients using caching should call NotifyWhenChanged upon connecting to
 	// the server. The server will stream back values whenever the client should
@@ -168,6 +169,11 @@
 	return
 }
 
+func (c implAgentClientStub) BlessingRootsDump(ctx *context.T, opts ...rpc.CallOpt) (o0 map[security.BlessingPattern][][]byte, err error) {
+	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingRootsDump", nil, []interface{}{&o0}, opts...)
+	return
+}
+
 func (c implAgentClientStub) BlessingRootsDebugString(ctx *context.T, opts ...rpc.CallOpt) (o0 string, err error) {
 	err = v23.GetClient(ctx).Call(ctx, c.name, "BlessingRootsDebugString", nil, []interface{}{&o0}, opts...)
 	return
@@ -269,6 +275,7 @@
 	BlessingStoreDebugString(*context.T, rpc.ServerCall) (string, error)
 	BlessingRootsAdd(ctx *context.T, call rpc.ServerCall, root []byte, pattern security.BlessingPattern) error
 	BlessingRootsRecognized(ctx *context.T, call rpc.ServerCall, root []byte, blessing string) error
+	BlessingRootsDump(*context.T, rpc.ServerCall) (map[security.BlessingPattern][][]byte, error)
 	BlessingRootsDebugString(*context.T, rpc.ServerCall) (string, error)
 	// Clients using caching should call NotifyWhenChanged upon connecting to
 	// the server. The server will stream back values whenever the client should
@@ -298,6 +305,7 @@
 	BlessingStoreDebugString(*context.T, rpc.ServerCall) (string, error)
 	BlessingRootsAdd(ctx *context.T, call rpc.ServerCall, root []byte, pattern security.BlessingPattern) error
 	BlessingRootsRecognized(ctx *context.T, call rpc.ServerCall, root []byte, blessing string) error
+	BlessingRootsDump(*context.T, rpc.ServerCall) (map[security.BlessingPattern][][]byte, error)
 	BlessingRootsDebugString(*context.T, rpc.ServerCall) (string, error)
 	// Clients using caching should call NotifyWhenChanged upon connecting to
 	// the server. The server will stream back values whenever the client should
@@ -399,6 +407,10 @@
 	return s.impl.BlessingRootsRecognized(ctx, call, i0, i1)
 }
 
+func (s implAgentServerStub) BlessingRootsDump(ctx *context.T, call rpc.ServerCall) (map[security.BlessingPattern][][]byte, error) {
+	return s.impl.BlessingRootsDump(ctx, call)
+}
+
 func (s implAgentServerStub) BlessingRootsDebugString(ctx *context.T, call rpc.ServerCall) (string, error) {
 	return s.impl.BlessingRootsDebugString(ctx, call)
 }
@@ -554,6 +566,12 @@
 			},
 		},
 		{
+			Name: "BlessingRootsDump",
+			OutArgs: []rpc.ArgDesc{
+				{"", ``}, // map[security.BlessingPattern][][]byte
+			},
+		},
+		{
 			Name: "BlessingRootsDebugString",
 			OutArgs: []rpc.ArgDesc{
 				{"", ``}, // string