Skip to content

Commit

Permalink
Store definitions locally in gateway mode
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Richardson <andrew.richardson@kaleido.io>
  • Loading branch information
awrichar committed Jun 23, 2022
1 parent e8e96c4 commit 2e21a3c
Show file tree
Hide file tree
Showing 24 changed files with 313 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- No down migration (can't add back NOT NULL constraint)
6 changes: 6 additions & 0 deletions db/migrations/postgres/000094_allow_local_identity.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BEGIN;
ALTER TABLE identities RENAME COLUMN messages_claim TO messages_claim_old;
ALTER TABLE identities ADD COLUMN messages_claim UUID;
UPDATE identities SET messages_claim = messages_claim_old;
ALTER TABLE identities DROP COLUMN messages_claim_old;
COMMIT;
1 change: 1 addition & 0 deletions db/migrations/sqlite/000094_allow_local_identity.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- No down migration (can't add back NOT NULL constraint)
4 changes: 4 additions & 0 deletions db/migrations/sqlite/000094_allow_local_identity.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE identities RENAME COLUMN messages_claim TO messages_claim_old;
ALTER TABLE identities ADD COLUMN messages_claim UUID;
UPDATE identities SET messages_claim = messages_claim_old;
ALTER TABLE identities DROP COLUMN messages_claim_old;
1 change: 1 addition & 0 deletions internal/coremsgs/en_error_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,5 @@ var (
MsgInvalidNamespaceUUID = ffe("FF10411", "Expected 'namespace:' prefix on ID '%s'", 400)
MsgSubscriptionIDInvalid = ffe("FF10412", "Invalid subscription ID: %s")
MsgActionOnlyValidMultiparty = ffe("FF10413", "This action is only valid in a multiparty namespace", 400)
MsgDefinitionRejected = ffe("FF10414", "Definition rejected")
)
13 changes: 12 additions & 1 deletion internal/defhandler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (

type DefinitionHandler interface {
HandleDefinitionBroadcast(ctx context.Context, state *core.BatchState, msg *core.Message, data core.DataArray, tx *fftypes.UUID) (HandlerResult, error)
HandleDefinition(ctx context.Context, state *core.BatchState, msg *core.Message, data *core.Data) error
}

type HandlerResult struct {
Expand Down Expand Up @@ -78,6 +79,7 @@ func (dma DefinitionMessageAction) String() string {

type definitionHandlers struct {
namespace string
multiparty bool
database database.Plugin
blockchain blockchain.Plugin
exchange dataexchange.Plugin // optional
Expand All @@ -87,12 +89,13 @@ type definitionHandlers struct {
contracts contracts.Manager
}

func NewDefinitionHandler(ctx context.Context, ns string, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager) (DefinitionHandler, error) {
func NewDefinitionHandler(ctx context.Context, ns string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager) (DefinitionHandler, error) {
if di == nil || bi == nil || dm == nil || im == nil || am == nil || cm == nil {
return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DefinitionHandler")
}
return &definitionHandlers{
namespace: ns,
multiparty: multiparty,
database: di,
blockchain: bi,
exchange: dx,
Expand Down Expand Up @@ -146,3 +149,11 @@ func (dh *definitionHandlers) getSystemBroadcastPayload(ctx context.Context, msg
res.SetBroadcastMessage(msg.Header.ID)
return true
}

func (dh *definitionHandlers) HandleDefinition(ctx context.Context, state *core.BatchState, msg *core.Message, data *core.Data) error {
result, err := dh.HandleDefinitionBroadcast(ctx, state, msg, core.DataArray{data}, nil)
if result.Action == ActionReject {
return i18n.WrapError(ctx, err, coremsgs.MsgDefinitionRejected)
}
return err
}
15 changes: 13 additions & 2 deletions internal/defhandler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func newTestDefinitionHandler(t *testing.T) (*definitionHandlers, *testDefinitio
mam := &assetmocks.Manager{}
mcm := &contractmocks.Manager{}
mbi.On("VerifierType").Return(core.VerifierTypeEthAddress).Maybe()
dh, _ := NewDefinitionHandler(context.Background(), "ns1", mdi, mbi, mdx, mdm, mim, mam, mcm)
dh, _ := NewDefinitionHandler(context.Background(), "ns1", false, mdi, mbi, mdx, mdm, mim, mam, mcm)
return dh.(*definitionHandlers), newTestDefinitionBatchState(t)
}

Expand All @@ -68,7 +68,7 @@ func (bs *testDefinitionBatchState) assertNoFinalizers() {
}

func TestInitFail(t *testing.T) {
_, err := NewDefinitionHandler(context.Background(), "", nil, nil, nil, nil, nil, nil, nil)
_, err := NewDefinitionHandler(context.Background(), "", false, nil, nil, nil, nil, nil, nil, nil)
assert.Regexp(t, "FF10128", err)
}

Expand Down Expand Up @@ -111,3 +111,14 @@ func TestActionEnum(t *testing.T) {
assert.Equal(t, "wait", fmt.Sprintf("%s", ActionWait))
assert.Equal(t, "unknown", fmt.Sprintf("%s", DefinitionMessageAction(999)))
}

func TestHandleDefinitionReject(t *testing.T) {
dh, bs := newTestDefinitionHandler(t)
err := dh.HandleDefinition(context.Background(), &bs.BatchState, &core.Message{
Header: core.MessageHeader{
Tag: "unknown",
},
}, &core.Data{})
assert.Error(t, err)
bs.assertNoFinalizers()
}
11 changes: 7 additions & 4 deletions internal/defhandler/identity_claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,11 @@ func (dh *definitionHandlers) handleIdentityClaim(ctx context.Context, state *co
return HandlerResult{Action: ActionWait}, nil
}

// Check signature verification
if err := dh.verifyClaimSignature(ctx, msg, identity, parent); err != nil {
return HandlerResult{Action: ActionReject}, err
// For multi-party namespaces, check that the claim message was appropriately signed
if dh.multiparty {
if err := dh.verifyClaimSignature(ctx, msg, identity, parent); err != nil {
return HandlerResult{Action: ActionReject}, err
}
}

existingIdentity, err := dh.database.GetIdentityByName(ctx, identity.Type, identity.Namespace, identity.Name)
Expand All @@ -175,7 +177,8 @@ func (dh *definitionHandlers) handleIdentityClaim(ctx context.Context, state *co
return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgDefRejectedConflict, "identity verifier", verifierLabel, existingVerifierLabel)
}

if parent != nil && identity.Type != core.IdentityTypeNode {
// For child identities in multi-party namespaces, check that the parent signed a verification message
if dh.multiparty && parent != nil && identity.Type != core.IdentityTypeNode {
// The verification might be passed into this function, if we confirm the verification second,
// or we might have to hunt for it, if we confirm the verification first.
if verificationID == nil {
Expand Down
77 changes: 73 additions & 4 deletions internal/defhandler/identity_claim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,11 @@ func testCustomClaimAndVerification(t *testing.T) (*core.Identity, *core.Identit

claimMsg := &core.Message{
Header: core.MessageHeader{
ID: custom1.Messages.Claim,
Type: core.MessageTypeDefinition,
Tag: core.SystemTagIdentityClaim,
Topics: core.FFStringArray{custom1.Topic()},
Namespace: "ns1",
ID: custom1.Messages.Claim,
Type: core.MessageTypeDefinition,
Tag: core.SystemTagIdentityClaim,
Topics: core.FFStringArray{custom1.Topic()},
SignerRef: core.SignerRef{
Author: custom1.DID,
Key: "0x12345",
Expand Down Expand Up @@ -173,6 +174,8 @@ func TestHandleDefinitionIdentityClaimCustomWithExistingParentVerificationOk(t *
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, false, nil).Once()
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -220,6 +223,8 @@ func TestHandleDefinitionIdentityClaimIdempotentReplay(t *testing.T) {
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, false, nil).Once()
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -254,6 +259,8 @@ func TestHandleDefinitionIdentityClaimFailInsertIdentity(t *testing.T) {
mdm := dh.data.(*datamocks.Manager)
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -284,6 +291,8 @@ func TestHandleDefinitionIdentityClaimVerificationDataFail(t *testing.T) {
mdm := dh.data.(*datamocks.Manager)
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(nil, false, fmt.Errorf("pop"))

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -314,6 +323,8 @@ func TestHandleDefinitionIdentityClaimVerificationMissingData(t *testing.T) {
mdm := dh.data.(*datamocks.Manager)
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -345,6 +356,8 @@ func TestHandleDefinitionIdentityClaimFailInsertVerifier(t *testing.T) {
mdm := dh.data.(*datamocks.Manager)
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{verifyData}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(verifyMsg.Header.ID, verifyMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
Expand Down Expand Up @@ -372,6 +385,8 @@ func TestHandleDefinitionIdentityClaimCustomMissingParentVerificationOk(t *testi
mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "0x12345").Return(nil, nil)
mdi.On("GetMessages", ctx, "ns1", mock.Anything).Return([]*core.Message{}, nil, nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionConfirm}, action) // Just wait for the verification to come in later
assert.NoError(t, err)
Expand All @@ -396,6 +411,8 @@ func TestHandleDefinitionIdentityClaimCustomParentVerificationFail(t *testing.T)
mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "0x12345").Return(nil, nil)
mdi.On("GetMessages", ctx, "ns1", mock.Anything).Return(nil, nil, fmt.Errorf("pop"))

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
assert.Regexp(t, "pop", err)
Expand All @@ -421,6 +438,8 @@ func TestHandleDefinitionIdentityClaimVerifierClash(t *testing.T) {
Hash: fftypes.NewRandB32(),
}, nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionReject}, action)
assert.Error(t, err)
Expand All @@ -444,6 +463,8 @@ func TestHandleDefinitionIdentityClaimVerifierError(t *testing.T) {
mdi.On("GetIdentityByID", ctx, "ns1", custom1.ID).Return(nil, nil)
mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "0x12345").Return(nil, fmt.Errorf("pop"))

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
assert.Regexp(t, "pop", err)
Expand All @@ -469,6 +490,8 @@ func TestHandleDefinitionIdentityClaimIdentityClash(t *testing.T) {
},
}, nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionReject}, action)
assert.Error(t, err)
Expand All @@ -491,6 +514,8 @@ func TestHandleDefinitionIdentityClaimIdentityError(t *testing.T) {
mdi.On("GetIdentityByName", ctx, custom1.Type, custom1.Namespace, custom1.Name).Return(nil, nil)
mdi.On("GetIdentityByID", ctx, "ns1", custom1.ID).Return(nil, fmt.Errorf("pop"))

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionRetry}, action)
assert.Regexp(t, "pop", err)
Expand All @@ -510,6 +535,8 @@ func TestHandleDefinitionIdentityMissingAuthor(t *testing.T) {
mim := dh.identity.(*identitymanagermocks.Manager)
mim.On("VerifyIdentityChain", ctx, custom1).Return(org1, false, nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionReject}, action)
assert.Error(t, err)
Expand All @@ -528,6 +555,8 @@ func TestHandleDefinitionIdentityClaimBadSignature(t *testing.T) {
mim := dh.identity.(*identitymanagermocks.Manager)
mim.On("VerifyIdentityChain", ctx, custom1).Return(org1, false, nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, claimMsg, core.DataArray{claimData}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionReject}, action)
assert.Error(t, err)
Expand Down Expand Up @@ -585,3 +614,43 @@ func TestHandleDefinitionIdentityClaimBadData(t *testing.T) {

bs.assertNoFinalizers()
}

func TestHandleDefinitionIdentityClaimGateway(t *testing.T) {
dh, bs := newTestDefinitionHandler(t)
ctx := context.Background()

custom1, org1, claimMsg, claimData, _, _ := testCustomClaimAndVerification(t)

mim := dh.identity.(*identitymanagermocks.Manager)
mim.On("VerifyIdentityChain", ctx, custom1).Return(org1, false, nil)

mdi := dh.database.(*databasemocks.Plugin)
mdi.On("GetIdentityByName", ctx, custom1.Type, custom1.Namespace, custom1.Name).Return(nil, nil)
mdi.On("GetIdentityByID", ctx, "ns1", custom1.ID).Return(nil, nil)
mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "0x12345").Return(nil, nil)
mdi.On("UpsertIdentity", ctx, mock.MatchedBy(func(identity *core.Identity) bool {
assert.Equal(t, *claimMsg.Header.ID, *identity.Messages.Claim)
assert.Nil(t, identity.Messages.Verification)
return true
}), database.UpsertOptimizationNew).Return(nil)
mdi.On("UpsertVerifier", ctx, mock.MatchedBy(func(verifier *core.Verifier) bool {
assert.Equal(t, core.VerifierTypeEthAddress, verifier.Type)
assert.Equal(t, "0x12345", verifier.Value)
assert.Equal(t, *custom1.ID, *verifier.Identity)
return true
}), database.UpsertOptimizationNew).Return(nil)
mdi.On("InsertEvent", mock.Anything, mock.MatchedBy(func(event *core.Event) bool {
return event.Type == core.EventTypeIdentityConfirmed
})).Return(nil)

err := dh.HandleDefinition(ctx, &bs.BatchState, claimMsg, claimData)
assert.NoError(t, err)
assert.Equal(t, bs.ConfirmedDIDClaims, []string{custom1.DID})

err = bs.RunFinalize(ctx)
assert.NoError(t, err)

mdi.AssertExpectations(t)
mim.AssertExpectations(t)

}
2 changes: 2 additions & 0 deletions internal/defhandler/identity_verification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func TestHandleDefinitionIdentityVerificationWithExistingClaimOk(t *testing.T) {
mdm := dh.data.(*datamocks.Manager)
mdm.On("GetMessageDataCached", ctx, mock.Anything).Return(core.DataArray{claimData}, true, nil)

dh.multiparty = true

bs.AddPendingConfirm(claimMsg.Header.ID, claimMsg)

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, verifyMsg, core.DataArray{verifyData}, fftypes.NewUUID())
Expand Down
2 changes: 2 additions & 0 deletions internal/defhandler/network_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ func TestHandleDeprecatedNodeDefinitionOK(t *testing.T) {
mdx := dh.exchange.(*dataexchangemocks.Plugin)
mdx.On("AddPeer", ctx, node.DX.Endpoint).Return(nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, msg, core.DataArray{data}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionConfirm}, action)
assert.NoError(t, err)
Expand Down
2 changes: 2 additions & 0 deletions internal/defhandler/network_org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ func TestHandleDeprecatedOrgDefinitionOK(t *testing.T) {
return event.Type == core.EventTypeIdentityConfirmed
})).Return(nil)

dh.multiparty = true

action, err := dh.HandleDefinitionBroadcast(ctx, &bs.BatchState, msg, core.DataArray{data}, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionConfirm}, action)
assert.NoError(t, err)
Expand Down
6 changes: 6 additions & 0 deletions internal/defsender/datatype.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ import (
"context"

"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly/internal/coremsgs"
"github.com/hyperledger/firefly/pkg/core"
)

func (bm *definitionSender) CreateDatatype(ctx context.Context, datatype *core.Datatype, waitConfirm bool) (*core.Message, error) {

if !bm.multiparty {
return nil, i18n.NewError(ctx, coremsgs.MsgActionOnlyValidMultiparty)
}

// Validate the input data definition data
datatype.ID = fftypes.NewUUID()
datatype.Created = fftypes.Now()
Expand Down
Loading

0 comments on commit 2e21a3c

Please sign in to comment.