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
}