273 lines
6.9 KiB
Go
273 lines
6.9 KiB
Go
package commonspace
|
|
|
|
import (
|
|
"context"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/cache"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
|
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
|
|
"hash/fnv"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
const CName = "common.commonspace"
|
|
|
|
var log = logger.NewNamed(CName)
|
|
|
|
func New() Service {
|
|
return &service{}
|
|
}
|
|
|
|
type Service interface {
|
|
CreateSpace(ctx context.Context, cache cache.TreeCache, payload SpaceCreatePayload) (Space, error)
|
|
DeriveSpace(ctx context.Context, cache cache.TreeCache, payload SpaceDerivePayload) (Space, error)
|
|
GetSpace(ctx context.Context, id string, cache cache.TreeCache) (sp Space, err error)
|
|
app.Component
|
|
}
|
|
|
|
type service struct {
|
|
config config.Space
|
|
configurationService nodeconf.Service
|
|
storageProvider storage.SpaceStorageProvider
|
|
}
|
|
|
|
func (s *service) Init(a *app.App) (err error) {
|
|
s.config = a.MustComponent(config.CName).(*config.Config).Space
|
|
s.storageProvider = a.MustComponent(storage.CName).(storage.SpaceStorageProvider)
|
|
s.configurationService = a.MustComponent(nodeconf.CName).(nodeconf.Service)
|
|
return nil
|
|
}
|
|
|
|
func (s *service) Name() (name string) {
|
|
return CName
|
|
}
|
|
|
|
func (s *service) CreateSpace(
|
|
ctx context.Context,
|
|
cache cache.TreeCache,
|
|
payload SpaceCreatePayload) (sp Space, err error) {
|
|
|
|
// unmarshalling signing and encryption keys
|
|
identity, err := payload.SigningKey.GetPublic().Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
encPubKey, err := payload.EncryptionKey.GetPublic().Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// preparing header and space id
|
|
bytes := make([]byte, 32)
|
|
_, err = rand.Read(bytes)
|
|
if err != nil {
|
|
return
|
|
}
|
|
header := &spacesyncproto.SpaceHeader{
|
|
Identity: identity,
|
|
Timestamp: time.Now().UnixNano(),
|
|
SpaceType: payload.SpaceType,
|
|
ReplicationKey: payload.ReplicationKey,
|
|
Seed: bytes,
|
|
}
|
|
marshalled, err := header.Marshal()
|
|
if err != nil {
|
|
return
|
|
}
|
|
id, err := cid.NewCIDFromBytes(marshalled)
|
|
if err != nil {
|
|
return
|
|
}
|
|
spaceId := NewSpaceId(id, payload.ReplicationKey)
|
|
|
|
// encrypting read key
|
|
hasher := fnv.New64()
|
|
_, err = hasher.Write(payload.ReadKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
readKeyHash := hasher.Sum64()
|
|
encReadKey, err := payload.EncryptionKey.GetPublic().Encrypt(payload.ReadKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// preparing acl
|
|
aclRoot := &aclrecordproto.ACLRoot{
|
|
Identity: identity,
|
|
EncryptionKey: encPubKey,
|
|
SpaceId: spaceId,
|
|
EncryptedReadKey: encReadKey,
|
|
DerivationScheme: "",
|
|
CurrentReadKeyHash: readKeyHash,
|
|
Timestamp: time.Now().UnixNano(),
|
|
}
|
|
rawWithId, err := marshalACLRoot(aclRoot, payload.SigningKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// creating storage
|
|
storageCreate := storage.SpaceStorageCreatePayload{
|
|
RecWithId: rawWithId,
|
|
SpaceHeader: header,
|
|
Id: id,
|
|
}
|
|
_, err = s.storageProvider.CreateSpaceStorage(storageCreate)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return s.GetSpace(ctx, spaceId, cache)
|
|
}
|
|
|
|
func (s *service) DeriveSpace(
|
|
ctx context.Context,
|
|
cache cache.TreeCache,
|
|
payload SpaceDerivePayload) (sp Space, err error) {
|
|
|
|
// unmarshalling signing and encryption keys
|
|
identity, err := payload.SigningKey.GetPublic().Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
signPrivKey, err := payload.SigningKey.Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
encPubKey, err := payload.EncryptionKey.GetPublic().Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
encPrivKey, err := payload.EncryptionKey.Raw()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// preparing replication key
|
|
hasher := fnv.New64()
|
|
_, err = hasher.Write(identity)
|
|
if err != nil {
|
|
return
|
|
}
|
|
repKey := hasher.Sum64()
|
|
|
|
// preparing header and space id
|
|
header := &spacesyncproto.SpaceHeader{
|
|
Identity: identity,
|
|
SpaceType: SpaceTypeDerived,
|
|
ReplicationKey: repKey,
|
|
}
|
|
marshalled, err := header.Marshal()
|
|
if err != nil {
|
|
return
|
|
}
|
|
id, err := cid.NewCIDFromBytes(marshalled)
|
|
if err != nil {
|
|
return
|
|
}
|
|
spaceId := NewSpaceId(id, repKey)
|
|
|
|
// deriving and encrypting read key
|
|
readKey, err := aclrecordproto.ACLReadKeyDerive(signPrivKey, encPrivKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
hasher = fnv.New64()
|
|
_, err = hasher.Write(readKey.Bytes())
|
|
if err != nil {
|
|
return
|
|
}
|
|
readKeyHash := hasher.Sum64()
|
|
encReadKey, err := payload.EncryptionKey.GetPublic().Encrypt(readKey.Bytes())
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// preparing acl
|
|
aclRoot := &aclrecordproto.ACLRoot{
|
|
Identity: identity,
|
|
EncryptionKey: encPubKey,
|
|
SpaceId: spaceId,
|
|
EncryptedReadKey: encReadKey,
|
|
DerivationScheme: "",
|
|
CurrentReadKeyHash: readKeyHash,
|
|
Timestamp: time.Now().UnixNano(),
|
|
}
|
|
rawWithId, err := marshalACLRoot(aclRoot, payload.SigningKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// creating storage
|
|
storageCreate := storage.SpaceStorageCreatePayload{
|
|
RecWithId: rawWithId,
|
|
SpaceHeader: header,
|
|
Id: id,
|
|
}
|
|
_, err = s.storageProvider.CreateSpaceStorage(storageCreate)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return s.GetSpace(ctx, spaceId, cache)
|
|
}
|
|
|
|
func (s *service) GetSpace(ctx context.Context, id string, cache cache.TreeCache) (Space, error) {
|
|
st, err := s.storageProvider.SpaceStorage(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lastConfiguration := s.configurationService.GetLast()
|
|
diffService := diffservice.NewDiffService(id, s.config.SyncPeriod, st, lastConfiguration, cache, log)
|
|
syncService := syncservice.NewSyncService(id, diffService, cache, lastConfiguration)
|
|
sp := &space{
|
|
id: id,
|
|
syncService: syncService,
|
|
diffService: diffService,
|
|
cache: cache,
|
|
storage: st,
|
|
}
|
|
if err := sp.Init(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
return sp, nil
|
|
}
|
|
|
|
func marshalACLRoot(aclRoot *aclrecordproto.ACLRoot, key signingkey.PrivKey) (rawWithId *aclrecordproto.RawACLRecordWithId, err error) {
|
|
marshalledRoot, err := aclRoot.Marshal()
|
|
if err != nil {
|
|
return
|
|
}
|
|
signature, err := key.Sign(marshalledRoot)
|
|
if err != nil {
|
|
return
|
|
}
|
|
raw := &aclrecordproto.RawACLRecord{
|
|
Payload: marshalledRoot,
|
|
Signature: signature,
|
|
}
|
|
marshalledRaw, err := raw.Marshal()
|
|
if err != nil {
|
|
return
|
|
}
|
|
aclHeadId, err := cid.NewCIDFromBytes(marshalledRaw)
|
|
if err != nil {
|
|
return
|
|
}
|
|
rawWithId = &aclrecordproto.RawACLRecordWithId{
|
|
Payload: marshalledRaw,
|
|
Id: aclHeadId,
|
|
}
|
|
return
|
|
}
|