From ed0a751d15e85937f27d0ab024bee09f6169b19b Mon Sep 17 00:00:00 2001 From: Sergey Cherepanov Date: Mon, 24 Apr 2023 14:59:15 +0200 Subject: [PATCH] wip: drpc access log --- commonspace/space.go | 99 ++- commonspace/spaceservice.go | 4 + commonspace/spacestorage/spacestorage_test.go | 654 ++++++++++++++++++ metric/log.go | 39 ++ metric/metric.go | 1 + 5 files changed, 763 insertions(+), 34 deletions(-) create mode 100644 metric/log.go diff --git a/commonspace/space.go b/commonspace/space.go index 6b512fd1..ecb81a80 100644 --- a/commonspace/space.go +++ b/commonspace/space.go @@ -3,6 +3,7 @@ package commonspace import ( "context" "errors" + "fmt" "github.com/anytypeio/any-sync/accountservice" "github.com/anytypeio/any-sync/app/logger" "github.com/anytypeio/any-sync/commonspace/headsync" @@ -20,6 +21,7 @@ import ( "github.com/anytypeio/any-sync/commonspace/spacestorage" "github.com/anytypeio/any-sync/commonspace/spacesyncproto" "github.com/anytypeio/any-sync/commonspace/syncstatus" + "github.com/anytypeio/any-sync/metric" "github.com/anytypeio/any-sync/net/peer" "github.com/anytypeio/any-sync/nodeconf" "github.com/anytypeio/any-sync/util/crypto" @@ -29,7 +31,6 @@ import ( "github.com/zeebo/errs" "go.uber.org/zap" "strconv" - "strings" "sync" "sync/atomic" "time" @@ -55,10 +56,21 @@ type SpaceCreatePayload struct { } type HandleMessage struct { - Id uint64 - Deadline time.Time - SenderId string - Message *spacesyncproto.ObjectSyncMessage + Id uint64 + ReceiveTime time.Time + StartHandlingTime time.Time + Deadline time.Time + SenderId string + Message *spacesyncproto.ObjectSyncMessage +} + +func (m HandleMessage) LogFields(fields ...zap.Field) []zap.Field { + return append(fields, + metric.SpaceId(m.Message.SpaceId), + metric.ObjectId(m.Message.ObjectId), + metric.QueueDur(m.StartHandlingTime.Sub(m.ReceiveTime)), + metric.TotalDur(time.Since(m.ReceiveTime)), + ) } type SpaceDerivePayload struct { @@ -77,7 +89,7 @@ type SpaceDescription struct { } func NewSpaceId(id string, repKey uint64) string { - return strings.Join([]string{id, strconv.FormatUint(repKey, 36)}, ".") + return fmt.Sprintf("%s.%s", id, strconv.FormatUint(repKey, 36)) } type Space interface { @@ -88,6 +100,7 @@ type Space interface { DebugAllHeads() []headsync.TreeHeads Description() (SpaceDescription, error) + DeriveTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) PutTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, listener updatelistener.UpdateListener) (t objecttree.ObjectTree, err error) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t objecttree.ObjectTree, err error) @@ -117,13 +130,13 @@ type space struct { headSync headsync.HeadSync syncStatus syncstatus.StatusUpdater storage spacestorage.SpaceStorage - treeManager *commonGetter + cache *commonGetter account accountservice.Service aclList *syncacl.SyncAcl - configuration nodeconf.Configuration + configuration nodeconf.NodeConf settingsObject settings.SettingsObject peerManager peermanager.PeerManager - treeBuilder objecttree.BuildObjectTreeFunc + metric metric.Metric handleQueue multiqueue.MultiQueue[HandleMessage] @@ -178,8 +191,8 @@ func (s *space) Init(ctx context.Context) (err error) { if err != nil { return } - s.aclList = syncacl.NewSyncAcl(aclList, s.objectSync.SyncClient().MessagePool()) - s.treeManager.AddObject(s.aclList) + s.aclList = syncacl.NewSyncAcl(aclList, s.objectSync.MessagePool()) + s.cache.AddObject(s.aclList) deletionState := settingsstate.NewObjectDeletionState(s.storage) deps := settings.Deps{ @@ -187,8 +200,6 @@ func (s *space) Init(ctx context.Context) (err error) { res, err := s.BuildTree(ctx, id, BuildTreeOpts{ Listener: listener, WaitTreeRemoteSync: false, - // space settings document should not have empty data - treeBuilder: objecttree.BuildObjectTree, }) log.Debug("building settings tree", zap.String("id", id), zap.String("spaceId", s.id)) if err != nil { @@ -198,7 +209,7 @@ func (s *space) Init(ctx context.Context) (err error) { return }, Account: s.account, - TreeManager: s.treeManager, + TreeGetter: s.cache, Store: s.storage, DeletionState: deletionState, Provider: s.headSync, @@ -206,12 +217,13 @@ func (s *space) Init(ctx context.Context) (err error) { OnSpaceDelete: s.onSpaceDelete, } s.settingsObject = settings.NewSettingsObject(deps, s.id) + s.objectSync.Init() s.headSync.Init(initialIds, deletionState) err = s.settingsObject.Init(ctx) if err != nil { return } - s.treeManager.AddObject(s.settingsObject) + s.cache.AddObject(s.settingsObject) s.syncStatus.Run() s.handleQueue = multiqueue.New[HandleMessage](s.handleMessage, 100) return nil @@ -243,6 +255,23 @@ func (s *space) DebugAllHeads() []headsync.TreeHeads { return s.headSync.DebugAllHeads() } +func (s *space) DeriveTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { + if s.isClosed.Load() { + err = ErrSpaceClosed + return + } + root, err := objecttree.DeriveObjectTreeRoot(payload, s.aclList) + if err != nil { + return + } + res = treestorage.TreeStorageCreatePayload{ + RootRawChange: root, + Changes: []*treechangeproto.RawTreeChangeWithId{root}, + Heads: []string{root.Id}, + } + return +} + func (s *space) CreateTree(ctx context.Context, payload objecttree.ObjectTreeCreatePayload) (res treestorage.TreeStorageCreatePayload, err error) { if s.isClosed.Load() { err = ErrSpaceClosed @@ -267,17 +296,16 @@ func (s *space) PutTree(ctx context.Context, payload treestorage.TreeStorageCrea return } deps := synctree.BuildDeps{ - SpaceId: s.id, - SyncClient: s.objectSync.SyncClient(), - Configuration: s.configuration, - HeadNotifiable: s.headSync, - Listener: listener, - AclList: s.aclList, - SpaceStorage: s.storage, - OnClose: s.onObjectClose, - SyncStatus: s.syncStatus, - PeerGetter: s.peerManager, - BuildObjectTree: s.treeBuilder, + SpaceId: s.id, + ObjectSync: s.objectSync, + Configuration: s.configuration, + HeadNotifiable: s.headSync, + Listener: listener, + AclList: s.aclList, + SpaceStorage: s.storage, + OnClose: s.onObjectClose, + SyncStatus: s.syncStatus, + PeerGetter: s.peerManager, } t, err = synctree.PutSyncTree(ctx, payload, deps) if err != nil { @@ -291,7 +319,6 @@ func (s *space) PutTree(ctx context.Context, payload treestorage.TreeStorageCrea type BuildTreeOpts struct { Listener updatelistener.UpdateListener WaitTreeRemoteSync bool - treeBuilder objecttree.BuildObjectTreeFunc } type HistoryTreeOpts struct { @@ -304,13 +331,10 @@ func (s *space) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t err = ErrSpaceClosed return } - treeBuilder := opts.treeBuilder - if treeBuilder == nil { - treeBuilder = s.treeBuilder - } + deps := synctree.BuildDeps{ SpaceId: s.id, - SyncClient: s.objectSync.SyncClient(), + ObjectSync: s.objectSync, Configuration: s.configuration, HeadNotifiable: s.headSync, Listener: opts.Listener, @@ -320,7 +344,6 @@ func (s *space) BuildTree(ctx context.Context, id string, opts BuildTreeOpts) (t SyncStatus: s.syncStatus, WaitTreeRemoteSync: opts.WaitTreeRemoteSync, PeerGetter: s.peerManager, - BuildObjectTree: treeBuilder, } if t, err = synctree.BuildSyncTreeOrGetRemote(ctx, id, deps); err != nil { return nil, err @@ -362,6 +385,7 @@ func (s *space) DeleteSpace(ctx context.Context, deleteChange *treechangeproto.R func (s *space) HandleMessage(ctx context.Context, hm HandleMessage) (err error) { threadId := hm.Message.ObjectId + hm.ReceiveTime = time.Now() if hm.Message.ReplyId != "" { threadId += hm.Message.ReplyId defer func() { @@ -378,12 +402,19 @@ func (s *space) HandleMessage(ctx context.Context, hm HandleMessage) (err error) } func (s *space) handleMessage(msg HandleMessage) { + var err error + msg.StartHandlingTime = time.Now() ctx := peer.CtxWithPeerId(context.Background(), msg.SenderId) ctx = logger.CtxWithFields(ctx, zap.Uint64("msgId", msg.Id), zap.String("senderId", msg.SenderId)) + defer func() { + s.metric.RequestLog(ctx, msg.LogFields(zap.Error(err))...) + }() + if !msg.Deadline.IsZero() { now := time.Now() if now.After(msg.Deadline) { log.InfoCtx(ctx, "skip message: deadline exceed") + err = context.DeadlineExceeded return } var cancel context.CancelFunc @@ -391,7 +422,7 @@ func (s *space) handleMessage(msg HandleMessage) { defer cancel() } - if err := s.objectSync.HandleMessage(ctx, msg.SenderId, msg.Message); err != nil { + if err = s.objectSync.HandleMessage(ctx, msg.SenderId, msg.Message); err != nil { if msg.Message.ObjectId != "" { // cleanup thread on error _ = s.handleQueue.CloseThread(msg.Message.ObjectId) diff --git a/commonspace/spaceservice.go b/commonspace/spaceservice.go index 4aafb0a2..eae68328 100644 --- a/commonspace/spaceservice.go +++ b/commonspace/spaceservice.go @@ -16,6 +16,7 @@ import ( "github.com/anytypeio/any-sync/commonspace/spacestorage" "github.com/anytypeio/any-sync/commonspace/spacesyncproto" "github.com/anytypeio/any-sync/commonspace/syncstatus" + "github.com/anytypeio/any-sync/metric" "github.com/anytypeio/any-sync/net/peer" "github.com/anytypeio/any-sync/net/pool" "github.com/anytypeio/any-sync/net/rpc/rpcerr" @@ -52,6 +53,7 @@ type spaceService struct { credentialProvider credentialprovider.CredentialProvider treeManager treemanager.TreeManager pool pool.Pool + metric metric.Metric } func (s *spaceService) Init(a *app.App) (err error) { @@ -68,6 +70,7 @@ func (s *spaceService) Init(a *app.App) (err error) { s.credentialProvider = credentialprovider.NewNoOp() } s.pool = a.MustComponent(pool.CName).(pool.Pool) + s.metric, _ = a.Component(metric.CName).(metric.Metric) return nil } @@ -181,6 +184,7 @@ func (s *spaceService) NewSpace(ctx context.Context, id string) (Space, error) { treeBuilder: builder, isClosed: spaceIsClosed, isDeleted: spaceIsDeleted, + metric: s.metric, } return sp, nil } diff --git a/commonspace/spacestorage/spacestorage_test.go b/commonspace/spacestorage/spacestorage_test.go index 82a25c1d..3e7b9959 100644 --- a/commonspace/spacestorage/spacestorage_test.go +++ b/commonspace/spacestorage/spacestorage_test.go @@ -1,2 +1,656 @@ package spacestorage +import ( + "crypto/rand" + "fmt" + "github.com/anytypeio/any-sync/commonspace/object/accountdata" + "github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto" + "github.com/anytypeio/any-sync/commonspace/object/tree/objecttree" + "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto" + "github.com/anytypeio/any-sync/commonspace/spacesyncproto" + "github.com/anytypeio/any-sync/util/cidutil" + "github.com/anytypeio/any-sync/util/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + rand2 "golang.org/x/exp/rand" + "strconv" + "strings" + "testing" + "time" +) + +func TestSuccessHeaderPayloadForSpaceCreate(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + _, rawHeaderWithId, err := rawHeaderWithId(accountKeys) + require.NoError(t, err) + err = validateCreateSpaceHeaderPayload(rawHeaderWithId) + require.NoError(t, err) +} + +func TestFailedHeaderPayloadForSpaceCreate_InvalidFormatSpaceId(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceHeaderSeed := make([]byte, 32) + _, err = rand.Read(spaceHeaderSeed) + require.NoError(t, err) + spaceHeaderPayload := make([]byte, 32) + _, err = rand.Read(spaceHeaderPayload) + require.NoError(t, err) + replicationKey := rand2.Uint64() + header := &spacesyncproto.SpaceHeader{ + Identity: identity, + Timestamp: time.Now().Unix(), + SpaceType: "SpaceType", + ReplicationKey: replicationKey, + Seed: spaceHeaderSeed, + SpaceHeaderPayload: spaceHeaderPayload, + } + marhalled, err := header.Marshal() + require.NoError(t, err) + signature, err := accountKeys.SignKey.Sign(marhalled) + require.NoError(t, err) + rawHeader := &spacesyncproto.RawSpaceHeader{ + SpaceHeader: marhalled, + Signature: signature, + } + marhalledRawHeader, err := rawHeader.Marshal() + require.NoError(t, err) + id, err := cidutil.NewCidFromBytes(marhalled) + require.NoError(t, err) + spaceId := fmt.Sprintf("%s%s", id, strconv.FormatUint(replicationKey, 36)) + rawHeaderWithId := &spacesyncproto.RawSpaceHeaderWithId{ + RawHeader: marhalledRawHeader, + Id: spaceId, + } + err = validateCreateSpaceHeaderPayload(rawHeaderWithId) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestFailedHeaderPayloadForSpaceCreate_CidIsWrong(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceHeaderSeed := make([]byte, 32) + _, err = rand.Read(spaceHeaderSeed) + require.NoError(t, err) + spaceHeaderPayload := make([]byte, 32) + _, err = rand.Read(spaceHeaderPayload) + require.NoError(t, err) + replicationKey := rand2.Uint64() + header := &spacesyncproto.SpaceHeader{ + Identity: identity, + Timestamp: time.Now().Unix(), + SpaceType: "SpaceType", + ReplicationKey: replicationKey, + Seed: spaceHeaderSeed, + SpaceHeaderPayload: spaceHeaderPayload, + } + marhalled, err := header.Marshal() + require.NoError(t, err) + signature, err := accountKeys.SignKey.Sign(marhalled) + require.NoError(t, err) + rawHeader := &spacesyncproto.RawSpaceHeader{ + SpaceHeader: marhalled, + Signature: signature, + } + marhalledRawHeader, err := rawHeader.Marshal() + require.NoError(t, err) + id := "faisdfjpiocpoakopkop34" + spaceId := fmt.Sprintf("%s.%s", id, strconv.FormatUint(replicationKey, 36)) + rawHeaderWithId := &spacesyncproto.RawSpaceHeaderWithId{ + RawHeader: marhalledRawHeader, + Id: spaceId, + } + err = validateCreateSpaceHeaderPayload(rawHeaderWithId) + assert.EqualErrorf(t, err, objecttree.ErrIncorrectCid.Error(), "Error should be: %v, got: %v", objecttree.ErrIncorrectCid, err) +} + +func TestFailedHeaderPayloadForSpaceCreate_SignedWithAnotherIdentity(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceHeaderSeed := make([]byte, 32) + _, err = rand.Read(spaceHeaderSeed) + require.NoError(t, err) + spaceHeaderPayload := make([]byte, 32) + _, err = rand.Read(spaceHeaderPayload) + require.NoError(t, err) + replicationKey := rand2.Uint64() + header := &spacesyncproto.SpaceHeader{ + Identity: identity, + Timestamp: time.Now().Unix(), + SpaceType: "SpaceType", + ReplicationKey: replicationKey, + Seed: spaceHeaderSeed, + SpaceHeaderPayload: spaceHeaderPayload, + } + marhalled, err := header.Marshal() + require.NoError(t, err) + anotherAccountKeys, err := accountdata.NewRandom() + signature, err := anotherAccountKeys.SignKey.Sign(marhalled) + require.NoError(t, err) + rawHeader := &spacesyncproto.RawSpaceHeader{ + SpaceHeader: marhalled, + Signature: signature, + } + marhalledRawHeader, err := rawHeader.Marshal() + require.NoError(t, err) + id := "faisdfjpiocpoakopkop34" + spaceId := fmt.Sprintf("%s.%s", id, strconv.FormatUint(replicationKey, 36)) + rawHeaderWithId := &spacesyncproto.RawSpaceHeaderWithId{ + RawHeader: marhalledRawHeader, + Id: spaceId, + } + err = validateCreateSpaceHeaderPayload(rawHeaderWithId) + assert.EqualErrorf(t, err, objecttree.ErrIncorrectCid.Error(), "Error should be: %v, got: %v", objecttree.ErrIncorrectCid, err) +} + +func TestSuccessAclPayloadSpace(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + spaceId := "AnySpaceId" + _, rawWithId, err := rawAclWithId(accountKeys, spaceId) + require.NoError(t, err) + validationSpaceId, err := validateCreateSpaceAclPayload(rawWithId) + require.Equal(t, validationSpaceId, spaceId) + require.NoError(t, err) +} + +func TestFailAclPayloadSpace_IncorrectCid(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + readKeyBytes := make([]byte, 32) + _, err = rand.Read(readKeyBytes) + require.NoError(t, err) + readKey, err := accountKeys.SignKey.GetPublic().Encrypt(readKeyBytes) + require.NoError(t, err) + masterKey, _, err := crypto.GenerateRandomEd25519KeyPair() + require.NoError(t, err) + rawIdentity, err := accountKeys.SignKey.GetPublic().Raw() + require.NoError(t, err) + identitySignature, err := masterKey.Sign(rawIdentity) + require.NoError(t, err) + rawMasterKey, err := masterKey.GetPublic().Marshall() + require.NoError(t, err) + aclRoot := aclrecordproto.AclRoot{ + Identity: identity, + MasterKey: rawMasterKey, + SpaceId: "SpaceId", + EncryptedReadKey: readKey, + Timestamp: time.Now().Unix(), + IdentitySignature: identitySignature, + } + marshalled, err := aclRoot.Marshal() + require.NoError(t, err) + signature, err := accountKeys.SignKey.Sign(marshalled) + rawAclRecord := &aclrecordproto.RawAclRecord{ + Payload: marshalled, + Signature: signature, + } + marshalledRaw, err := rawAclRecord.Marshal() + require.NoError(t, err) + aclHeadId := "rand" + rawWithId := &aclrecordproto.RawAclRecordWithId{ + Payload: marshalledRaw, + Id: aclHeadId, + } + _, err = validateCreateSpaceAclPayload(rawWithId) + assert.EqualErrorf(t, err, objecttree.ErrIncorrectCid.Error(), "Error should be: %v, got: %v", objecttree.ErrIncorrectCid, err) +} + +func TestFailedAclPayloadSpace_IncorrectSignature(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + readKeyBytes := make([]byte, 32) + _, err = rand.Read(readKeyBytes) + require.NoError(t, err) + readKey, err := accountKeys.SignKey.GetPublic().Encrypt(readKeyBytes) + require.NoError(t, err) + masterKey, _, err := crypto.GenerateRandomEd25519KeyPair() + require.NoError(t, err) + rawIdentity, err := accountKeys.SignKey.GetPublic().Raw() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + identitySignature, err := masterKey.Sign(rawIdentity) + require.NoError(t, err) + rawMasterKey, err := masterKey.GetPublic().Raw() + require.NoError(t, err) + aclRoot := aclrecordproto.AclRoot{ + Identity: identity, + MasterKey: rawMasterKey, + SpaceId: "SpaceId", + EncryptedReadKey: readKey, + Timestamp: time.Now().Unix(), + IdentitySignature: identitySignature, + } + marshalled, err := aclRoot.Marshal() + require.NoError(t, err) + rawAclRecord := &aclrecordproto.RawAclRecord{ + Payload: marshalled, + Signature: marshalled, + } + marshalledRaw, err := rawAclRecord.Marshal() + require.NoError(t, err) + aclHeadId, err := cidutil.NewCidFromBytes(marshalledRaw) + require.NoError(t, err) + rawWithId := &aclrecordproto.RawAclRecordWithId{ + Payload: marshalledRaw, + Id: aclHeadId, + } + _, err = validateCreateSpaceAclPayload(rawWithId) + assert.NotNil(t, err) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestFailedAclPayloadSpace_IncorrectIdentitySignature(t *testing.T) { + spaceId := "AnySpaceId" + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + readKeyBytes := make([]byte, 32) + _, err = rand.Read(readKeyBytes) + if err != nil { + return + } + readKey, err := accountKeys.SignKey.GetPublic().Encrypt(readKeyBytes) + if err != nil { + return + } + masterKey, _, err := crypto.GenerateRandomEd25519KeyPair() + if err != nil { + return + } + masterPubKey := masterKey.GetPublic() + identity, err := accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + rawMasterKey, err := masterPubKey.Marshall() + if err != nil { + return + } + aclRoot := aclrecordproto.AclRoot{ + Identity: identity, + MasterKey: rawMasterKey, + SpaceId: spaceId, + EncryptedReadKey: readKey, + Timestamp: time.Now().Unix(), + IdentitySignature: identity, + } + marshalled, err := aclRoot.Marshal() + if err != nil { + return + } + signature, err := accountKeys.SignKey.Sign(marshalled) + rawAclRecord := &aclrecordproto.RawAclRecord{ + Payload: marshalled, + Signature: signature, + } + marshalledRaw, err := rawAclRecord.Marshal() + if err != nil { + return + } + aclHeadId, err := cidutil.NewCidFromBytes(marshalledRaw) + if err != nil { + return + } + rawWithId := &aclrecordproto.RawAclRecordWithId{ + Payload: marshalledRaw, + Id: aclHeadId, + } + _, err = validateCreateSpaceAclPayload(rawWithId) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestSuccessSettingsPayloadSpace(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceSettingsSeed := make([]byte, 32) + _, err = rand.Read(spaceSettingsSeed) + require.NoError(t, err) + changePayload := make([]byte, 32) + _, err = rand.Read(changePayload) + require.NoError(t, err) + spaceId := "SpaceId" + rootChange := &treechangeproto.RootChange{ + AclHeadId: "AclHeadId", + SpaceId: spaceId, + ChangeType: "ChangeType", + Timestamp: time.Now().Unix(), + Seed: spaceSettingsSeed, + Identity: identity, + ChangePayload: changePayload, + } + marshalledChange, err := rootChange.Marshal() + require.NoError(t, err) + signature, err := accountKeys.SignKey.Sign(marshalledChange) + require.NoError(t, err) + raw := &treechangeproto.RawTreeChange{ + Payload: marshalledChange, + Signature: signature, + } + marshalledRawChange, err := raw.Marshal() + id, err := cidutil.NewCidFromBytes(marshalledRawChange) + require.NoError(t, err) + rawIdChange := &treechangeproto.RawTreeChangeWithId{ + RawChange: marshalledRawChange, + Id: id, + } + _, validationSpaceId, err := validateCreateSpaceSettingsPayload(rawIdChange) + require.Equal(t, validationSpaceId, spaceId) + require.NoError(t, err) +} + +func TestFailSettingsPayloadSpace_InvalidSignature(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceSettingsSeed := make([]byte, 32) + _, err = rand.Read(spaceSettingsSeed) + require.NoError(t, err) + changePayload := make([]byte, 32) + _, err = rand.Read(changePayload) + require.NoError(t, err) + rootChange := &treechangeproto.RootChange{ + AclHeadId: "AclHeadId", + SpaceId: "SpaceId", + ChangeType: "ChangeType", + Timestamp: time.Now().Unix(), + Seed: spaceSettingsSeed, + Identity: identity, + ChangePayload: changePayload, + } + marshalledChange, err := rootChange.Marshal() + require.NoError(t, err) + raw := &treechangeproto.RawTreeChange{ + Payload: marshalledChange, + Signature: marshalledChange, + } + marshalledRawChange, err := raw.Marshal() + id, err := cidutil.NewCidFromBytes(marshalledRawChange) + require.NoError(t, err) + rawIdChange := &treechangeproto.RawTreeChangeWithId{ + RawChange: marshalledRawChange, + Id: id, + } + _, _, err = validateCreateSpaceSettingsPayload(rawIdChange) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestFailSettingsPayloadSpace_InvalidCid(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + identity, err := accountKeys.SignKey.GetPublic().Marshall() + require.NoError(t, err) + spaceSettingsSeed := make([]byte, 32) + _, err = rand.Read(spaceSettingsSeed) + require.NoError(t, err) + changePayload := make([]byte, 32) + _, err = rand.Read(changePayload) + require.NoError(t, err) + rootChange := &treechangeproto.RootChange{ + AclHeadId: "AclHeadId", + SpaceId: "SpaceId", + ChangeType: "ChangeType", + Timestamp: time.Now().Unix(), + Seed: spaceSettingsSeed, + Identity: identity, + ChangePayload: changePayload, + } + marshalledChange, err := rootChange.Marshal() + require.NoError(t, err) + signature, err := accountKeys.SignKey.Sign(marshalledChange) + require.NoError(t, err) + raw := &treechangeproto.RawTreeChange{ + Payload: marshalledChange, + Signature: signature, + } + marshalledRawChange, err := raw.Marshal() + id := "id" + require.NoError(t, err) + rawIdChange := &treechangeproto.RawTreeChangeWithId{ + RawChange: marshalledRawChange, + Id: id, + } + _, _, err = validateCreateSpaceSettingsPayload(rawIdChange) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestSuccessSameIds(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + spaceId, rawHeaderWithId, err := rawHeaderWithId(accountKeys) + require.NoError(t, err) + aclHeadId, rawAclWithId, err := rawAclWithId(accountKeys, spaceId) + require.NoError(t, err) + rawSettingsPayload, err := rawSettingsPayload(accountKeys, spaceId, aclHeadId) + spacePayload := SpaceStorageCreatePayload{ + AclWithId: rawAclWithId, + SpaceHeaderWithId: rawHeaderWithId, + SpaceSettingsWithId: rawSettingsPayload, + } + err = ValidateSpaceStorageCreatePayload(spacePayload) + require.NoError(t, err) +} + +func TestFailWithAclWrongSpaceId(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + spaceId, rawHeaderWithId, err := rawHeaderWithId(accountKeys) + require.NoError(t, err) + aclHeadId, rawAclWithId, err := rawAclWithId(accountKeys, "spaceId") + require.NoError(t, err) + rawSettingsPayload, err := rawSettingsPayload(accountKeys, spaceId, aclHeadId) + spacePayload := SpaceStorageCreatePayload{ + AclWithId: rawAclWithId, + SpaceHeaderWithId: rawHeaderWithId, + SpaceSettingsWithId: rawSettingsPayload, + } + err = ValidateSpaceStorageCreatePayload(spacePayload) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestFailWithSettingsWrongSpaceId(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + spaceId, rawHeaderWithId, err := rawHeaderWithId(accountKeys) + require.NoError(t, err) + aclHeadId, rawAclWithId, err := rawAclWithId(accountKeys, spaceId) + require.NoError(t, err) + rawSettingsPayload, err := rawSettingsPayload(accountKeys, "spaceId", aclHeadId) + spacePayload := SpaceStorageCreatePayload{ + AclWithId: rawAclWithId, + SpaceHeaderWithId: rawHeaderWithId, + SpaceSettingsWithId: rawSettingsPayload, + } + err = ValidateSpaceStorageCreatePayload(spacePayload) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func TestFailWithWrongAclHeadIdInSettingsPayload(t *testing.T) { + accountKeys, err := accountdata.NewRandom() + require.NoError(t, err) + spaceId, rawHeaderWithId, err := rawHeaderWithId(accountKeys) + require.NoError(t, err) + _, rawAclWithId, err := rawAclWithId(accountKeys, spaceId) + require.NoError(t, err) + rawSettingsPayload, err := rawSettingsPayload(accountKeys, spaceId, "aclHeadId") + spacePayload := SpaceStorageCreatePayload{ + AclWithId: rawAclWithId, + SpaceHeaderWithId: rawHeaderWithId, + SpaceSettingsWithId: rawSettingsPayload, + } + err = ValidateSpaceStorageCreatePayload(spacePayload) + assert.EqualErrorf(t, err, ErrIncorrectSpaceHeader.Error(), "Error should be: %v, got: %v", ErrIncorrectSpaceHeader, err) +} + +func rawSettingsPayload(accountKeys *accountdata.AccountKeys, spaceId, aclHeadId string) (rawIdChange *treechangeproto.RawTreeChangeWithId, err error) { + identity, err := accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + spaceSettingsSeed := make([]byte, 32) + _, err = rand.Read(spaceSettingsSeed) + if err != nil { + return + } + changePayload := make([]byte, 32) + _, err = rand.Read(changePayload) + if err != nil { + return + } + rootChange := &treechangeproto.RootChange{ + AclHeadId: aclHeadId, + SpaceId: spaceId, + ChangeType: "ChangeType", + Timestamp: time.Now().Unix(), + Seed: spaceSettingsSeed, + Identity: identity, + ChangePayload: changePayload, + } + marshalledChange, err := rootChange.Marshal() + if err != nil { + return + } + signature, err := accountKeys.SignKey.Sign(marshalledChange) + if err != nil { + return + } + raw := &treechangeproto.RawTreeChange{ + Payload: marshalledChange, + Signature: signature, + } + marshalledRawChange, err := raw.Marshal() + id, err := cidutil.NewCidFromBytes(marshalledRawChange) + if err != nil { + return + } + rawIdChange = &treechangeproto.RawTreeChangeWithId{ + RawChange: marshalledRawChange, + Id: id, + } + + return +} + +func rawAclWithId(accountKeys *accountdata.AccountKeys, spaceId string) (aclHeadId string, rawWithId *aclrecordproto.RawAclRecordWithId, err error) { + readKeyBytes := make([]byte, 32) + _, err = rand.Read(readKeyBytes) + if err != nil { + return + } + readKey, err := accountKeys.SignKey.GetPublic().Encrypt(readKeyBytes) + if err != nil { + return + } + masterKey, _, err := crypto.GenerateRandomEd25519KeyPair() + identity, err := accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + masterPubKey := masterKey.GetPublic() + rawIdentity, err := accountKeys.SignKey.GetPublic().Raw() + if err != nil { + return + } + identitySignature, err := masterKey.Sign(rawIdentity) + if err != nil { + return + } + rawMasterKey, err := masterPubKey.Marshall() + if err != nil { + return + } + aclRoot := aclrecordproto.AclRoot{ + Identity: identity, + MasterKey: rawMasterKey, + SpaceId: spaceId, + EncryptedReadKey: readKey, + Timestamp: time.Now().Unix(), + IdentitySignature: identitySignature, + } + marshalled, err := aclRoot.Marshal() + if err != nil { + return + } + signature, err := accountKeys.SignKey.Sign(marshalled) + rawAclRecord := &aclrecordproto.RawAclRecord{ + Payload: marshalled, + Signature: signature, + } + marshalledRaw, err := rawAclRecord.Marshal() + if err != nil { + return + } + aclHeadId, err = cidutil.NewCidFromBytes(marshalledRaw) + if err != nil { + return + } + rawWithId = &aclrecordproto.RawAclRecordWithId{ + Payload: marshalledRaw, + Id: aclHeadId, + } + + return +} + +func rawHeaderWithId(accountKeys *accountdata.AccountKeys) (spaceId string, rawWithId *spacesyncproto.RawSpaceHeaderWithId, err error) { + identity, err := accountKeys.SignKey.GetPublic().Marshall() + if err != nil { + return + } + spaceHeaderSeed := make([]byte, 32) + _, err = rand.Read(spaceHeaderSeed) + if err != nil { + return + } + spaceHeaderPayload := make([]byte, 32) + _, err = rand.Read(spaceHeaderPayload) + if err != nil { + return + } + replicationKey := rand2.Uint64() + header := &spacesyncproto.SpaceHeader{ + Identity: identity, + Timestamp: time.Now().Unix(), + SpaceType: "SpaceType", + ReplicationKey: replicationKey, + Seed: spaceHeaderSeed, + SpaceHeaderPayload: spaceHeaderPayload, + } + marhalled, err := header.Marshal() + if err != nil { + return + } + signature, err := accountKeys.SignKey.Sign(marhalled) + if err != nil { + return + } + rawHeader := &spacesyncproto.RawSpaceHeader{ + SpaceHeader: marhalled, + Signature: signature, + } + marhalledRawHeader, err := rawHeader.Marshal() + if err != nil { + return + } + id, err := cidutil.NewCidFromBytes(marhalledRawHeader) + if err != nil { + return + } + spaceId = fmt.Sprintf("%s.%s", id, strconv.FormatUint(replicationKey, 36)) + rawWithId = &spacesyncproto.RawSpaceHeaderWithId{ + RawHeader: marhalledRawHeader, + Id: spaceId, + } + + return +} diff --git a/metric/log.go b/metric/log.go new file mode 100644 index 00000000..12a18424 --- /dev/null +++ b/metric/log.go @@ -0,0 +1,39 @@ +package metric + +import ( + "go.uber.org/zap" + "golang.org/x/net/context" + "time" +) + +func Method(val string) zap.Field { + return zap.String("rpc", val) +} + +func QueueDur(val time.Duration) zap.Field { + return zap.Int64("queueMs", val.Milliseconds()) +} + +func TotalDur(val time.Duration) zap.Field { + return zap.Int64("totalMs", val.Milliseconds()) +} + +func SpaceId(val string) zap.Field { + return zap.String("spaceId", val) +} + +func ObjectId(val string) zap.Field { + return zap.String("objectId", val) +} + +func Identity(val string) zap.Field { + return zap.String("identity", val) +} + +func IP(val string) zap.Field { + return zap.String("ip", val) +} + +func (m *metric) RequestLog(ctx context.Context, fields ...zap.Field) { + m.rpcLog.InfoCtx(ctx, "", fields...) +} diff --git a/metric/metric.go b/metric/metric.go index d64e81da..32f29ae5 100644 --- a/metric/metric.go +++ b/metric/metric.go @@ -24,6 +24,7 @@ func New() Metric { type Metric interface { Registry() *prometheus.Registry WrapDRPCHandler(h drpc.Handler) drpc.Handler + RequestLog(ctx context.Context, fields ...zap.Field) app.ComponentRunnable }