Skip to content

Commit

Permalink
[FAB-9515] Add indirect SatisfiesPrincipal MSP caching
Browse files Browse the repository at this point in the history
The MSP cache, caches the invocation of SatisfiesPrincipal,
however - many of the invocations of SatisfiesPrincipal are
invoked indirectly via the identities that the MSP returns via DeserializeIdentity(),
and these invocations are not cached, because the bccsp msp's identity simply invokes
the bccsp MSP's SatisfiesPrincipal() which bypasses the cache.

This change set wraps the identities returned by DeserializeIdentity with
a struct that overrides the SatisfiesPrincipal method and redirects
the SatisfiesPrincipal invocations to the cache instead of the real MSP.

Change-Id: I73d38675dd385897bdef7c0d7a927f05cf747501
Signed-off-by: yacovm <yacovm@il.ibm.com>
  • Loading branch information
yacovm committed Apr 14, 2018
1 parent e51bd92 commit 155f5fd
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 7 deletions.
22 changes: 19 additions & 3 deletions msp/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,37 @@ type cachedMSP struct {
spcMutex sync.Mutex // synchronize access to cache
}

type cachedIdentity struct {
msp.Identity
cache *cachedMSP
}

func (id *cachedIdentity) SatisfiesPrincipal(principal *pmsp.MSPPrincipal) error {
return id.cache.SatisfiesPrincipal(id.Identity, principal)
}

func (c *cachedMSP) DeserializeIdentity(serializedIdentity []byte) (msp.Identity, error) {
c.dicMutex.Lock()
cached, ok := c.deserializeIdentityCache.Get(string(serializedIdentity))
id, ok := c.deserializeIdentityCache.Get(string(serializedIdentity))
c.dicMutex.Unlock()
if ok {
return cached.(msp.Identity), nil
return &cachedIdentity{
cache: c,
Identity: id.(msp.Identity),
}, nil
}

id, err := c.MSP.DeserializeIdentity(serializedIdentity)
if err == nil {
c.dicMutex.Lock()
defer c.dicMutex.Unlock()
c.deserializeIdentityCache.Add(string(serializedIdentity), id)
return &cachedIdentity{
cache: c,
Identity: id.(msp.Identity),
}, nil
}
return id, err
return nil, err
}

func (c *cachedMSP) Setup(config *pmsp.MSPConfig) error {
Expand Down
35 changes: 33 additions & 2 deletions msp/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func TestDeserializeIdentity(t *testing.T) {
}
id, err := wrappedMSP.DeserializeIdentity(sIdentity)
assert.NoError(t, err)
assert.Equal(t, expectedIdentity, id)
assert.Equal(t, expectedIdentity, id.(*cachedIdentity).Identity)
}(wrappedMSP, i)
}
wg.Wait()
Expand All @@ -158,7 +158,7 @@ func TestDeserializeIdentity(t *testing.T) {
// Check the same object is returned
id, err := wrappedMSP.DeserializeIdentity(serializedIdentity)
assert.NoError(t, err)
assert.True(t, mockIdentity == id)
assert.True(t, mockIdentity == id.(*cachedIdentity).Identity)
mockMSP.AssertExpectations(t)

// Check id is not cached
Expand Down Expand Up @@ -213,6 +213,37 @@ func TestValidate(t *testing.T) {
assert.False(t, ok)
}

func TestSatisfiesPrincipalIndirectCall(t *testing.T) {
mockMSP := &mocks.MockMSP{}
mockMSPPrincipal := &msp2.MSPPrincipal{PrincipalClassification: msp2.MSPPrincipal_IDENTITY, Principal: []byte{1, 2, 3}}

mockIdentity := &mocks.MockIdentity{ID: "Alice"}
mockIdentity.On("SatisfiesPrincipal", mockMSPPrincipal).Run(func(_ mock.Arguments) {
panic("shouldn't have invoked the identity method")
})
mockMSP.On("DeserializeIdentity", mock.Anything).Return(mockIdentity, nil).Once()
mockIdentity.On("GetIdentifier").Return(&msp.IdentityIdentifier{Mspid: "MSP", Id: "Alice"})

cache, err := New(mockMSP)
assert.NoError(t, err)

// First invocation of the SatisfiesPrincipal returns an error
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(errors.New("error: foo")).Once()
// Second invocation returns nil
mockMSP.On("SatisfiesPrincipal", mockIdentity, mockMSPPrincipal).Return(nil).Once()

// Test that cache returns the correct value
err = cache.SatisfiesPrincipal(mockIdentity, mockMSPPrincipal)
assert.Equal(t, "error: foo", err.Error())
// Get the identity we test the caching on
identity, err := cache.DeserializeIdentity([]byte{1, 2, 3})
assert.NoError(t, err)
// Ensure the identity returned answers what the cached MSP answers.
// If the invocation doesn't hit the cache, it will return nil instead of an error.
err = identity.SatisfiesPrincipal(mockMSPPrincipal)
assert.Equal(t, "error: foo", err.Error())
}

func TestSatisfiesPrincipal(t *testing.T) {
mockMSP := &mocks.MockMSP{}
i, err := New(mockMSP)
Expand Down
4 changes: 2 additions & 2 deletions msp/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ func (*MockIdentity) Serialize() ([]byte, error) {
panic("implement me")
}

func (*MockIdentity) SatisfiesPrincipal(principal *pmsp.MSPPrincipal) error {
panic("implement me")
func (m *MockIdentity) SatisfiesPrincipal(principal *pmsp.MSPPrincipal) error {
return m.Called(principal).Error(0)
}

type MockSigningIdentity struct {
Expand Down

0 comments on commit 155f5fd

Please sign in to comment.