Merge pull request #8 from anytypeio/space-sync

This commit is contained in:
Mikhail Rakhmanov 2022-09-29 13:33:37 +02:00 committed by GitHub
commit 58b4dc760e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
131 changed files with 14745 additions and 16638 deletions

10
.gitignore vendored
View File

@ -11,5 +11,15 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# Golang vendor folder
vendor
# Intelli-J files
.idea
# MacOS file that stores custom attributes of its containing folder,
# such as folder view options, icon positions, and other visual information
.DS_Store
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/

View File

@ -12,25 +12,24 @@ endif
export PATH=$(GOPATH)/bin:$(shell echo $$PATH) export PATH=$(GOPATH)/bin:$(shell echo $$PATH)
# TODO: folders were changed, so we should update Makefile and protos generation
proto: proto:
@echo 'Generating protobuf packages (Go)...' @echo 'Generating protobuf packages (Go)...'
# Uncomment if needed # Uncomment if needed
@$(eval ROOT_PKG := pkg) @$(eval ROOT_PKG := pkg)
@$(eval GOGO_START := GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1) @$(eval GOGO_START := GOGO_NO_UNDERSCORE=1 GOGO_EXPORT_ONEOF_INTERFACE=1)
@$(eval P_ACL_CHANGES_PATH_PB := $(ROOT_PKG)/acl/aclchanges/aclpb) @$(eval P_ACL_RECORDS_PATH_PB := $(ROOT_PKG)/acl/aclrecordproto)
@$(eval P_TREE_CHANGES_PATH_PB := $(ROOT_PKG)/acl/treechangeproto)
@$(eval P_SYNC_CHANGES_PATH_PB := syncproto) @$(eval P_SYNC_CHANGES_PATH_PB := syncproto)
@$(eval P_TEST_CHANGES_PATH_PB := $(ROOT_PKG)/acl/testutils/testchanges) @$(eval P_TEST_CHANGES_PATH_PB := $(ROOT_PKG)/acl/testutils/testchanges)
@$(eval P_TIMESTAMP := Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types) @$(eval P_ACL_RECORDS := M$(P_ACL_RECORDS_PATH_PB)/protos/aclrecord.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/$(P_ACL_RECORDS_PATH_PB))
@$(eval P_STRUCT := Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types) @$(eval P_TREE_CHANGES := M$(P_TREE_CHANGES_PATH_PB)/protos/treechange.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/$(P_TREE_CHANGES_PATH_PB))
@$(eval P_ACL_CHANGES := M$(P_ACL_CHANGES_PATH_PB)/protos/aclchanges.proto=github.com/anytypeio/go-anytype-infrastructure-experiments/$(P_ACL_CHANGES_PATH_PB))
# use if needed $(eval PKGMAP := $$(P_TIMESTAMP),$$(P_STRUCT)) $(GOGO_START) protoc --gogofaster_out=:. $(P_ACL_RECORDS_PATH_PB)/protos/*.proto
$(GOGO_START) protoc --gogofaster_out=:. $(P_ACL_CHANGES_PATH_PB)/protos/*.proto; mv $(P_ACL_CHANGES_PATH_PB)/protos/*.go $(P_ACL_CHANGES_PATH_PB) $(GOGO_START) protoc --gogofaster_out=:. $(P_TREE_CHANGES_PATH_PB)/protos/*.proto
$(GOGO_START) protoc --gogofaster_out=:. $(P_TEST_CHANGES_PATH_PB)/proto/*.proto $(GOGO_START) protoc --gogofaster_out=:. $(P_TEST_CHANGES_PATH_PB)/proto/*.proto
$(eval PKGMAP := $$(P_ACL_CHANGES)) $(eval PKGMAP := $$(P_TREE_CHANGES),$$(P_ACL_RECORDS))
$(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. $(P_SYNC_CHANGES_PATH_PB)/proto/*.proto $(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. --go-drpc_out=protolib=github.com/gogo/protobuf:. common/commonspace/spacesyncproto/protos/*.proto
$(GOGO_START) protoc --gogofaster_out=$(PKGMAP):. service/space/spacesync/protos/*.proto
build: build:
@$(eval FLAGS := $$(shell govvv -flags -pkg github.com/anytypeio/go-anytype-infrastructure-experiments/app)) @$(eval FLAGS := $$(shell govvv -flags -pkg github.com/anytypeio/go-anytype-infrastructure-experiments/app))

View File

@ -27,7 +27,7 @@ var (
type Component interface { type Component interface {
// Init will be called first // Init will be called first
// When returned error is not nil - app start will be aborted // When returned error is not nil - app start will be aborted
Init(ctx context.Context, a *App) (err error) Init(a *App) (err error)
// Name must return unique service name // Name must return unique service name
Name() (name string) Name() (name string)
} }
@ -157,7 +157,7 @@ func (app *App) Start(ctx context.Context) (err error) {
} }
for i, s := range app.components { for i, s := range app.components {
if err = s.Init(ctx, app); err != nil { if err = s.Init(app); err != nil {
closeServices(i) closeServices(i)
return fmt.Errorf("can't init service '%s': %v", s.Name(), err) return fmt.Errorf("can't init service '%s': %v", s.Name(), err)
} }

View File

@ -131,7 +131,7 @@ type testComponent struct {
ids testIds ids testIds
} }
func (t *testComponent) Init(ctx context.Context, a *App) error { func (t *testComponent) Init(a *App) error {
t.ids.initId = t.seq.New() t.ids.initId = t.seq.New()
return t.err return t.err
} }

View File

@ -6,20 +6,15 @@ import (
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/dialer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/rpc/server"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/secure"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config" "github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/node/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/api" "github.com/anytypeio/go-anytype-infrastructure-experiments/node/nodespace"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/configuration"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/document"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/dialer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/rpc/server"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/secure"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/node"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/message"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/requesthandler"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache"
"go.uber.org/zap" "go.uber.org/zap"
"net/http" "net/http"
_ "net/http/pprof" _ "net/http/pprof"
@ -94,16 +89,18 @@ func main() {
func Bootstrap(a *app.App) { func Bootstrap(a *app.App) {
a.Register(account.New()). a.Register(account.New()).
Register(node.New()). // TODO: add space storage provider from node side
Register(nodeconf.New()).
Register(secure.New()). Register(secure.New()).
Register(server.New()).
Register(dialer.New()). Register(dialer.New()).
Register(pool.NewPool()). Register(pool.New()).
Register(storage.New()). Register(nodespace.New()).
Register(configuration.New()). Register(commonspace.New()).
Register(document.New()). Register(server.New())
Register(message.New()).
Register(requesthandler.New()). //Register(document.New()).
Register(treecache.New()). //Register(message.New()).
Register(api.New()) //Register(requesthandler.New()).
//Register(treecache.New()).
//Register(api.New())
} }

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config" "github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/peer" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/peer"
@ -75,8 +76,8 @@ func main() {
} }
createDir() createDir()
} }
for _, cfg := range configs { for idx, cfg := range configs {
path := fmt.Sprintf("%s/%s.yml", configsPath, cfg.Account.PeerId) path := fmt.Sprintf("%s/config%d.yml", configsPath, idx+1)
bytes, err := yaml.Marshal(cfg) bytes, err := yaml.Marshal(cfg)
if err != nil { if err != nil {
panic(fmt.Sprintf("could not marshal the keys: %v", err)) panic(fmt.Sprintf("could not marshal the keys: %v", err))
@ -100,15 +101,12 @@ func genConfig(addresses []string, apiPort string) (config.Config, error) {
return config.Config{}, err return config.Config{}, err
} }
encKeyDecoder := encryptionkey.NewRSAPrivKeyDecoder() encEncKey, err := keys.EncodeKeyToString(encKey)
signKeyDecoder := signingkey.NewEDPrivKeyDecoder()
encEncKey, err := encKeyDecoder.EncodeToString(encKey)
if err != nil { if err != nil {
return config.Config{}, err return config.Config{}, err
} }
encSignKey, err := signKeyDecoder.EncodeToString(signKey) encSignKey, err := keys.EncodeKeyToString(signKey)
if err != nil { if err != nil {
return config.Config{}, err return config.Config{}, err
} }
@ -132,5 +130,9 @@ func genConfig(addresses []string, apiPort string) (config.Config, error) {
APIServer: config.APIServer{ APIServer: config.APIServer{
Port: apiPort, Port: apiPort,
}, },
Space: config.Space{
GCTTL: 60,
SyncPeriod: 10,
},
}, nil }, nil
} }

View File

@ -1,9 +1,7 @@
nodes: nodes:
- grpcAddresses: - grpcAddresses:
- "127.0.0.1:4430" - "127.0.0.1:4430"
- "127.0.0.1:4431"
apiPort: "8080" apiPort: "8080"
- grpcAddresses: - grpcAddresses:
- "127.0.0.1:4432" - "127.0.0.1:4431"
- "127.0.0.1:4433"
apiPort: "8081" apiPort: "8081"

13
common/account/service.go Normal file
View File

@ -0,0 +1,13 @@
package account
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
)
const CName = "common.account"
type Service interface {
app.Component
Account() *account.AccountData
}

29
common/commonspace/cache/treecache.go vendored Normal file
View File

@ -0,0 +1,29 @@
package cache
import (
"context"
"errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
)
const CName = "commonspace.cache"
var ErrSpaceNotFound = errors.New("space not found")
type TreeContainer interface {
Tree() tree.ObjectTree
}
type TreeResult struct {
Release func()
TreeContainer TreeContainer
}
type BuildFunc = func(ctx context.Context, id string, listener synctree.UpdateListener) (tree.ObjectTree, error)
type TreeCache interface {
app.ComponentRunnable
GetTree(ctx context.Context, spaceId, treeId string) (TreeResult, error)
}

View File

@ -0,0 +1,183 @@
package diffservice
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/cache"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
"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/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/rpc/rpcerr"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ldiff"
"go.uber.org/zap"
"strings"
"time"
)
type DiffService interface {
HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error)
UpdateHeads(id string, heads []string)
RemoveObject(id string)
Init(objectIds []string)
Close() (err error)
}
type diffService struct {
spaceId string
periodicSync *periodicSync
storage storage.SpaceStorage
nconf nodeconf.Configuration
diff ldiff.Diff
cache cache.TreeCache
log *zap.Logger
syncPeriod int
}
func NewDiffService(
spaceId string,
syncPeriod int,
storage storage.SpaceStorage,
nconf nodeconf.Configuration,
cache cache.TreeCache,
log *zap.Logger) DiffService {
return &diffService{
spaceId: spaceId,
storage: storage,
nconf: nconf,
cache: cache,
log: log,
syncPeriod: syncPeriod,
}
}
func (d *diffService) Init(objectIds []string) {
d.periodicSync = newPeriodicSync(d.syncPeriod, d.sync, d.log.With(zap.String("spaceId", d.spaceId)))
d.diff = ldiff.New(16, 16)
d.fillDiff(objectIds)
}
func (d *diffService) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) {
return remotediff.HandleRangeRequest(ctx, d.diff, req)
}
func (d *diffService) UpdateHeads(id string, heads []string) {
d.diff.Set(ldiff.Element{
Id: id,
Head: concatStrings(heads),
})
}
func (d *diffService) RemoveObject(id string) {
// TODO: add space document to remove ids
d.diff.RemoveId(id)
}
func (d *diffService) Close() (err error) {
d.periodicSync.Close()
return nil
}
func (d *diffService) sync(ctx context.Context) error {
st := time.Now()
// diffing with responsible peers according to configuration
peers, err := d.nconf.ResponsiblePeers(ctx, d.spaceId)
if err != nil {
return err
}
for _, p := range peers {
if err := d.syncWithPeer(ctx, p); err != nil {
d.log.Error("can't sync with peer", zap.String("peer", p.Id()), zap.Error(err))
}
}
d.log.Info("synced", zap.String("spaceId", d.spaceId), zap.Duration("dur", time.Since(st)))
return nil
}
func (d *diffService) syncWithPeer(ctx context.Context, p peer.Peer) (err error) {
cl := spacesyncproto.NewDRPCSpaceClient(p)
rdiff := remotediff.NewRemoteDiff(d.spaceId, cl)
newIds, changedIds, removedIds, err := d.diff.Diff(ctx, rdiff)
err = rpcerr.Unwrap(err)
if err != nil && err != spacesyncproto.ErrSpaceMissing {
return err
}
if err == spacesyncproto.ErrSpaceMissing {
return d.sendPushSpaceRequest(ctx, cl)
}
d.pingTreesInCache(ctx, newIds)
d.pingTreesInCache(ctx, changedIds)
d.log.Info("sync done:", zap.Int("newIds", len(newIds)),
zap.Int("changedIds", len(changedIds)),
zap.Int("removedIds", len(removedIds)))
return
}
func (d *diffService) pingTreesInCache(ctx context.Context, trees []string) {
for _, tId := range trees {
_, _ = d.cache.GetTree(ctx, d.spaceId, tId)
}
}
func (d *diffService) fillDiff(objectIds []string) {
var els = make([]ldiff.Element, 0, len(objectIds))
for _, id := range objectIds {
st, err := d.storage.TreeStorage(id)
if err != nil {
continue
}
heads, err := st.Heads()
if err != nil {
continue
}
els = append(els, ldiff.Element{
Id: id,
Head: concatStrings(heads),
})
}
d.diff.Set(els...)
}
func (d *diffService) sendPushSpaceRequest(ctx context.Context, cl spacesyncproto.DRPCSpaceClient) (err error) {
aclStorage, err := d.storage.ACLStorage()
if err != nil {
return
}
root, err := aclStorage.Root()
if err != nil {
return
}
header, err := d.storage.SpaceHeader()
if err != nil {
return
}
_, err = cl.PushSpace(ctx, &spacesyncproto.PushSpaceRequest{
SpaceId: d.spaceId,
SpaceHeader: header,
AclRoot: root,
})
return
}
func concatStrings(strs []string) string {
var (
b strings.Builder
totalLen int
)
for _, s := range strs {
totalLen += len(s)
}
b.Grow(totalLen)
for _, s := range strs {
b.WriteString(s)
}
return b.String()
}

View File

@ -0,0 +1,58 @@
package diffservice
import (
"context"
"go.uber.org/zap"
"time"
)
func newPeriodicSync(periodSeconds int, sync func(ctx context.Context) error, l *zap.Logger) *periodicSync {
ctx, cancel := context.WithCancel(context.Background())
ps := &periodicSync{
log: l,
sync: sync,
syncCtx: ctx,
syncCancel: cancel,
syncLoopDone: make(chan struct{}),
}
go ps.syncLoop(periodSeconds)
return ps
}
type periodicSync struct {
log *zap.Logger
sync func(ctx context.Context) error
syncCtx context.Context
syncCancel context.CancelFunc
syncLoopDone chan struct{}
}
func (p *periodicSync) syncLoop(periodSeconds int) {
period := time.Duration(periodSeconds) * time.Second
defer close(p.syncLoopDone)
doSync := func() {
ctx, cancel := context.WithTimeout(p.syncCtx, time.Minute)
defer cancel()
if err := p.sync(ctx); err != nil {
p.log.Warn("periodic sync error", zap.Error(err))
}
}
doSync()
if period > 0 {
ticker := time.NewTicker(period)
defer ticker.Stop()
for {
select {
case <-p.syncCtx.Done():
return
case <-ticker.C:
doSync()
}
}
}
}
func (p *periodicSync) Close() {
p.syncCancel()
<-p.syncLoopDone
}

View File

@ -0,0 +1,98 @@
package remotediff
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ldiff"
)
type Client interface {
HeadSync(ctx context.Context, in *spacesyncproto.HeadSyncRequest) (*spacesyncproto.HeadSyncResponse, error)
}
func NewRemoteDiff(spaceId string, client Client) ldiff.Remote {
return remote{
spaceId: spaceId,
client: client,
}
}
type remote struct {
spaceId string
client Client
}
func (r remote) Ranges(ctx context.Context, ranges []ldiff.Range, resBuf []ldiff.RangeResult) (results []ldiff.RangeResult, err error) {
results = resBuf[:0]
pbRanges := make([]*spacesyncproto.HeadSyncRange, 0, len(ranges))
for _, rg := range ranges {
pbRanges = append(pbRanges, &spacesyncproto.HeadSyncRange{
From: rg.From,
To: rg.To,
Limit: uint32(rg.Limit),
})
}
req := &spacesyncproto.HeadSyncRequest{
SpaceId: r.spaceId,
Ranges: pbRanges,
}
resp, err := r.client.HeadSync(ctx, req)
if err != nil {
return
}
for _, rr := range resp.Results {
var elms []ldiff.Element
if len(rr.Elements) > 0 {
elms = make([]ldiff.Element, 0, len(rr.Elements))
}
for _, e := range rr.Elements {
elms = append(elms, ldiff.Element{
Id: e.Id,
Head: e.Head,
})
}
results = append(results, ldiff.RangeResult{
Hash: rr.Hash,
Elements: elms,
Count: int(rr.Count),
})
}
return
}
func HandleRangeRequest(ctx context.Context, d ldiff.Diff, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) {
ranges := make([]ldiff.Range, 0, len(req.Ranges))
for _, reqRange := range req.Ranges {
ranges = append(ranges, ldiff.Range{
From: reqRange.From,
To: reqRange.To,
Limit: int(reqRange.Limit),
})
}
res, err := d.Ranges(ctx, ranges, nil)
if err != nil {
return
}
resp = &spacesyncproto.HeadSyncResponse{
Results: make([]*spacesyncproto.HeadSyncResult, 0, len(res)),
}
for _, rangeRes := range res {
var elements []*spacesyncproto.HeadSyncResultElement
if len(rangeRes.Elements) > 0 {
elements = make([]*spacesyncproto.HeadSyncResultElement, 0, len(rangeRes.Elements))
for _, el := range rangeRes.Elements {
elements = append(elements, &spacesyncproto.HeadSyncResultElement{
Id: el.Id,
Head: el.Head,
})
}
}
resp.Results = append(resp.Results, &spacesyncproto.HeadSyncResult{
Hash: rangeRes.Hash,
Elements: elements,
Count: uint32(rangeRes.Count),
})
}
return
}

View File

@ -0,0 +1,41 @@
package remotediff
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ldiff"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestRemote(t *testing.T) {
ldLocal := ldiff.New(8, 8)
ldRemote := ldiff.New(8, 8)
for i := 0; i < 100; i++ {
el := ldiff.Element{
Id: fmt.Sprint(i),
Head: fmt.Sprint(i),
}
ldRemote.Set(el)
if i%10 != 0 {
ldLocal.Set(el)
}
}
rd := NewRemoteDiff("1", &mockClient{l: ldRemote})
newIds, changedIds, removedIds, err := ldLocal.Diff(context.Background(), rd)
require.NoError(t, err)
assert.Len(t, newIds, 10)
assert.Len(t, changedIds, 0)
assert.Len(t, removedIds, 0)
}
type mockClient struct {
l ldiff.Diff
}
func (m *mockClient) HeadSync(ctx context.Context, in *spacesyncproto.HeadSyncRequest) (*spacesyncproto.HeadSyncResponse, error) {
return HandleRangeRequest(ctx, m.l, in)
}

View File

@ -0,0 +1,23 @@
package commonspace
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
)
type RpcHandler interface {
HeadSync(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (*spacesyncproto.HeadSyncResponse, error)
Stream(stream spacesyncproto.DRPCSpace_StreamStream) error
}
type rpcHandler struct {
s *space
}
func (r *rpcHandler) HeadSync(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (*spacesyncproto.HeadSyncResponse, error) {
return r.s.DiffService().HandleRangeRequest(ctx, req)
}
func (r *rpcHandler) Stream(stream spacesyncproto.DRPCSpace_StreamStream) (err error) {
return r.s.SyncService().StreamPool().AddAndReadStreamSync(stream)
}

View File

@ -0,0 +1,274 @@
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) (sp Space, err error)
app.Component
}
type service struct {
config config.Space
configurationService nodeconf.Service
storageProvider storage.SpaceStorageProvider
cache cache.TreeCache
}
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)
s.cache = a.MustComponent(cache.CName).(cache.TreeCache)
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)
}
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)
}
func (s *service) GetSpace(ctx context.Context, id string) (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, s.cache, log)
syncService := syncservice.NewSyncService(id, diffService, s.cache, lastConfiguration)
sp := &space{
id: id,
syncService: syncService,
diffService: diffService,
cache: s.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
}

150
common/commonspace/space.go Normal file
View File

@ -0,0 +1,150 @@
package commonspace
import (
"context"
"fmt"
"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/commonspace/synctree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
treestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"sync"
)
type SpaceCreatePayload struct {
SigningKey signingkey.PrivKey
EncryptionKey encryptionkey.PrivKey
SpaceType string
ReadKey []byte
ReplicationKey uint64
}
const SpaceTypeDerived = "derived.space"
type SpaceDerivePayload struct {
SigningKey signingkey.PrivKey
EncryptionKey encryptionkey.PrivKey
}
func NewSpaceId(id string, repKey uint64) string {
return fmt.Sprintf("%s.%d", id, repKey)
}
type Space interface {
Id() string
SpaceSyncRpc() RpcHandler
DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener synctree.UpdateListener) (tree.ObjectTree, error)
CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener synctree.UpdateListener) (tree.ObjectTree, error)
BuildTree(ctx context.Context, id string, listener synctree.UpdateListener) (tree.ObjectTree, error)
Close() error
}
type space struct {
id string
mu sync.RWMutex
rpc *rpcHandler
syncService syncservice.SyncService
diffService diffservice.DiffService
storage storage.SpaceStorage
cache cache.TreeCache
aclList list.ACLList
}
func (s *space) Id() string {
return s.id
}
func (s *space) Init(ctx context.Context) (err error) {
s.rpc = &rpcHandler{s: s}
initialIds, err := s.storage.StoredIds()
if err != nil {
return
}
s.diffService.Init(initialIds)
s.syncService.Init()
return nil
}
func (s *space) SpaceSyncRpc() RpcHandler {
return s.rpc
}
func (s *space) SyncService() syncservice.SyncService {
return s.syncService
}
func (s *space) DiffService() diffservice.DiffService {
return s.diffService
}
func (s *space) DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener synctree.UpdateListener) (tree.ObjectTree, error) {
return synctree.DeriveSyncTree(ctx, payload, s.syncService, listener, s.aclList, s.storage.CreateTreeStorage)
}
func (s *space) CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener synctree.UpdateListener) (tree.ObjectTree, error) {
return synctree.CreateSyncTree(ctx, payload, s.syncService, listener, s.aclList, s.storage.CreateTreeStorage)
}
func (s *space) BuildTree(ctx context.Context, id string, listener synctree.UpdateListener) (t tree.ObjectTree, err error) {
getTreeRemote := func() (*spacesyncproto.ObjectSyncMessage, error) {
// TODO: add empty context handling (when this is not happening due to head update)
peerId, err := syncservice.GetPeerIdFromStreamContext(ctx)
if err != nil {
return nil, err
}
return s.syncService.StreamPool().SendSync(
peerId,
spacesyncproto.WrapFullRequest(&spacesyncproto.ObjectFullSyncRequest{}, nil, id, ""),
)
}
store, err := s.storage.TreeStorage(id)
if err != nil && err != treestorage.ErrUnknownTreeId {
return
}
if err == treestorage.ErrUnknownTreeId {
var resp *spacesyncproto.ObjectSyncMessage
resp, err = getTreeRemote()
if err != nil {
return
}
fullSyncResp := resp.GetContent().GetFullSyncResponse()
payload := treestorage.TreeStorageCreatePayload{
TreeId: resp.TreeId,
RootRawChange: resp.RootChange,
Changes: fullSyncResp.Changes,
Heads: fullSyncResp.Heads,
}
// basically building tree with inmemory storage and validating that it was without errors
err = tree.ValidateRawTree(payload, s.aclList)
if err != nil {
return
}
// now we are sure that we can save it to the storage
store, err = s.storage.CreateTreeStorage(payload)
if err != nil {
return
}
}
return synctree.BuildSyncTree(ctx, s.syncService, store.(treestorage.TreeStorage), listener, s.aclList)
}
func (s *space) Close() error {
s.diffService.Close()
return s.syncService.Close()
}

View File

@ -0,0 +1,14 @@
package spacesyncproto
import (
"errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/rpc/rpcerr"
)
var (
errGroup = rpcerr.ErrGroup(ErrCodes_ErrorOffset)
ErrUnexpected = errGroup.Register(errors.New("unexpected error"), uint64(ErrCodes_Unexpected))
ErrSpaceMissing = errGroup.Register(errors.New("space is missing"), uint64(ErrCodes_SpaceMissing))
ErrSpaceExists = errGroup.Register(errors.New("space exists"), uint64(ErrCodes_SpaceMissing))
)

View File

@ -0,0 +1,120 @@
syntax = "proto3";
package anySpace;
option go_package = "common/commonspace/spacesyncproto";
import "pkg/acl/treechangeproto/protos/treechange.proto";
import "pkg/acl/aclrecordproto/protos/aclrecord.proto";
enum ErrCodes {
Unexpected = 0;
SpaceMissing = 1;
SpaceExists = 2;
ErrorOffset = 100;
}
service Space {
// HeadSync compares all objects and their hashes in a space
rpc HeadSync(HeadSyncRequest) returns (HeadSyncResponse);
// PushSpace sends new space to the node
rpc PushSpace(PushSpaceRequest) returns (PushSpaceResponse);
// Stream opens object sync stream with node or client
rpc Stream(stream ObjectSyncMessage) returns (stream ObjectSyncMessage);
}
// HeadSyncRange presenting a request for one range
message HeadSyncRange {
uint64 from = 1;
uint64 to = 2;
uint32 limit = 3;
}
// HeadSyncResult presenting a response for one range
message HeadSyncResult {
bytes hash = 1;
repeated HeadSyncResultElement elements = 2;
uint32 count = 3;
}
// HeadSyncResultElement presenting state of one object
message HeadSyncResultElement {
string id = 1;
string head = 2;
}
// HeadSyncRequest is a request for HeadSync
message HeadSyncRequest {
string spaceId = 1;
repeated HeadSyncRange ranges = 2;
}
// HeadSyncResponse is a response for HeadSync
message HeadSyncResponse {
repeated HeadSyncResult results = 1;
}
// ObjectSyncMessage is a message sent on object sync
message ObjectSyncMessage {
string spaceId = 1;
ObjectSyncContentValue content = 2;
treechange.RawTreeChangeWithId rootChange = 3;
string treeId = 4;
string trackingId = 5;
// string identity = 5;
// string peerSignature = 6;
}
// ObjectSyncContentValue provides different types for object sync
message ObjectSyncContentValue {
oneof value {
ObjectHeadUpdate headUpdate = 1;
ObjectFullSyncRequest fullSyncRequest = 2;
ObjectFullSyncResponse fullSyncResponse = 3;
ObjectErrorResponse errorResponse = 4;
}
}
// ObjectHeadUpdate is a message sent on document head update
message ObjectHeadUpdate {
repeated string heads = 1;
repeated treechange.RawTreeChangeWithId changes = 2;
repeated string snapshotPath = 3;
}
// ObjectHeadUpdate is a message sent when document needs full sync
message ObjectFullSyncRequest {
repeated string heads = 1;
repeated treechange.RawTreeChangeWithId changes = 2;
repeated string snapshotPath = 3;
}
// ObjectFullSyncResponse is a message sent as a response for a specific full sync
message ObjectFullSyncResponse {
repeated string heads = 1;
repeated treechange.RawTreeChangeWithId changes = 2;
repeated string snapshotPath = 3;
}
// ObjectErrorResponse is an error sent as a response for a full sync request
message ObjectErrorResponse {
string error = 1;
}
// PushSpaceRequest is a request to add space on a node containing only one acl record
message PushSpaceRequest {
string spaceId = 1;
SpaceHeader spaceHeader = 2;
aclrecord.RawACLRecordWithId aclRoot = 3;
}
// PushSpaceResponse is an empty response
message PushSpaceResponse {}
// SpaceHeader is a header for a space
message SpaceHeader {
bytes identity = 1;
int64 timestamp = 2;
string spaceType = 3;
uint64 replicationKey = 4;
bytes seed = 5;
}

View File

@ -0,0 +1,45 @@
package spacesyncproto
import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
type SpaceStream = DRPCSpace_StreamStream
func WrapHeadUpdate(update *ObjectHeadUpdate, rootChange *treechangeproto.RawTreeChangeWithId, treeId, trackingId string) *ObjectSyncMessage {
return &ObjectSyncMessage{
Content: &ObjectSyncContentValue{
Value: &ObjectSyncContentValue_HeadUpdate{HeadUpdate: update},
},
RootChange: rootChange,
TreeId: treeId,
}
}
func WrapFullRequest(request *ObjectFullSyncRequest, rootChange *treechangeproto.RawTreeChangeWithId, treeId, trackingId string) *ObjectSyncMessage {
return &ObjectSyncMessage{
Content: &ObjectSyncContentValue{
Value: &ObjectSyncContentValue_FullSyncRequest{FullSyncRequest: request},
},
RootChange: rootChange,
TreeId: treeId,
}
}
func WrapFullResponse(response *ObjectFullSyncResponse, rootChange *treechangeproto.RawTreeChangeWithId, treeId, trackingId string) *ObjectSyncMessage {
return &ObjectSyncMessage{
Content: &ObjectSyncContentValue{
Value: &ObjectSyncContentValue_FullSyncResponse{FullSyncResponse: response},
},
RootChange: rootChange,
TreeId: treeId,
}
}
func WrapError(err error, rootChange *treechangeproto.RawTreeChangeWithId, treeId, trackingId string) *ObjectSyncMessage {
return &ObjectSyncMessage{
Content: &ObjectSyncContentValue{
Value: &ObjectSyncContentValue_ErrorResponse{ErrorResponse: &ObjectErrorResponse{Error: err.Error()}},
},
RootChange: rootChange,
TreeId: treeId,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
// Code generated by protoc-gen-go-drpc. DO NOT EDIT.
// protoc-gen-go-drpc version: v0.0.32
// source: common/commonspace/spacesyncproto/protos/spacesync.proto
package spacesyncproto
import (
bytes "bytes"
context "context"
errors "errors"
jsonpb "github.com/gogo/protobuf/jsonpb"
proto "github.com/gogo/protobuf/proto"
drpc "storj.io/drpc"
drpcerr "storj.io/drpc/drpcerr"
)
type drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto struct{}
func (drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto) Marshal(msg drpc.Message) ([]byte, error) {
return proto.Marshal(msg.(proto.Message))
}
func (drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto) Unmarshal(buf []byte, msg drpc.Message) error {
return proto.Unmarshal(buf, msg.(proto.Message))
}
func (drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto) JSONMarshal(msg drpc.Message) ([]byte, error) {
var buf bytes.Buffer
err := new(jsonpb.Marshaler).Marshal(&buf, msg.(proto.Message))
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func (drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto) JSONUnmarshal(buf []byte, msg drpc.Message) error {
return jsonpb.Unmarshal(bytes.NewReader(buf), msg.(proto.Message))
}
type DRPCSpaceClient interface {
DRPCConn() drpc.Conn
HeadSync(ctx context.Context, in *HeadSyncRequest) (*HeadSyncResponse, error)
PushSpace(ctx context.Context, in *PushSpaceRequest) (*PushSpaceResponse, error)
Stream(ctx context.Context) (DRPCSpace_StreamClient, error)
}
type drpcSpaceClient struct {
cc drpc.Conn
}
func NewDRPCSpaceClient(cc drpc.Conn) DRPCSpaceClient {
return &drpcSpaceClient{cc}
}
func (c *drpcSpaceClient) DRPCConn() drpc.Conn { return c.cc }
func (c *drpcSpaceClient) HeadSync(ctx context.Context, in *HeadSyncRequest) (*HeadSyncResponse, error) {
out := new(HeadSyncResponse)
err := c.cc.Invoke(ctx, "/anySpace.Space/HeadSync", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcSpaceClient) PushSpace(ctx context.Context, in *PushSpaceRequest) (*PushSpaceResponse, error) {
out := new(PushSpaceResponse)
err := c.cc.Invoke(ctx, "/anySpace.Space/PushSpace", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
func (c *drpcSpaceClient) Stream(ctx context.Context) (DRPCSpace_StreamClient, error) {
stream, err := c.cc.NewStream(ctx, "/anySpace.Space/Stream", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{})
if err != nil {
return nil, err
}
x := &drpcSpace_StreamClient{stream}
return x, nil
}
type DRPCSpace_StreamClient interface {
drpc.Stream
Send(*ObjectSyncMessage) error
Recv() (*ObjectSyncMessage, error)
}
type drpcSpace_StreamClient struct {
drpc.Stream
}
func (x *drpcSpace_StreamClient) Send(m *ObjectSyncMessage) error {
return x.MsgSend(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{})
}
func (x *drpcSpace_StreamClient) Recv() (*ObjectSyncMessage, error) {
m := new(ObjectSyncMessage)
if err := x.MsgRecv(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}); err != nil {
return nil, err
}
return m, nil
}
func (x *drpcSpace_StreamClient) RecvMsg(m *ObjectSyncMessage) error {
return x.MsgRecv(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{})
}
type DRPCSpaceServer interface {
HeadSync(context.Context, *HeadSyncRequest) (*HeadSyncResponse, error)
PushSpace(context.Context, *PushSpaceRequest) (*PushSpaceResponse, error)
Stream(DRPCSpace_StreamStream) error
}
type DRPCSpaceUnimplementedServer struct{}
func (s *DRPCSpaceUnimplementedServer) HeadSync(context.Context, *HeadSyncRequest) (*HeadSyncResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCSpaceUnimplementedServer) PushSpace(context.Context, *PushSpaceRequest) (*PushSpaceResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
func (s *DRPCSpaceUnimplementedServer) Stream(DRPCSpace_StreamStream) error {
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
type DRPCSpaceDescription struct{}
func (DRPCSpaceDescription) NumMethods() int { return 3 }
func (DRPCSpaceDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
switch n {
case 0:
return "/anySpace.Space/HeadSync", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCSpaceServer).
HeadSync(
ctx,
in1.(*HeadSyncRequest),
)
}, DRPCSpaceServer.HeadSync, true
case 1:
return "/anySpace.Space/PushSpace", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCSpaceServer).
PushSpace(
ctx,
in1.(*PushSpaceRequest),
)
}, DRPCSpaceServer.PushSpace, true
case 2:
return "/anySpace.Space/Stream", drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return nil, srv.(DRPCSpaceServer).
Stream(
&drpcSpace_StreamStream{in1.(drpc.Stream)},
)
}, DRPCSpaceServer.Stream, true
default:
return "", nil, nil, nil, false
}
}
func DRPCRegisterSpace(mux drpc.Mux, impl DRPCSpaceServer) error {
return mux.Register(impl, DRPCSpaceDescription{})
}
type DRPCSpace_HeadSyncStream interface {
drpc.Stream
SendAndClose(*HeadSyncResponse) error
}
type drpcSpace_HeadSyncStream struct {
drpc.Stream
}
func (x *drpcSpace_HeadSyncStream) SendAndClose(m *HeadSyncResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCSpace_PushSpaceStream interface {
drpc.Stream
SendAndClose(*PushSpaceResponse) error
}
type drpcSpace_PushSpaceStream struct {
drpc.Stream
}
func (x *drpcSpace_PushSpaceStream) SendAndClose(m *PushSpaceResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}); err != nil {
return err
}
return x.CloseSend()
}
type DRPCSpace_StreamStream interface {
drpc.Stream
Send(*ObjectSyncMessage) error
Recv() (*ObjectSyncMessage, error)
}
type drpcSpace_StreamStream struct {
drpc.Stream
}
func (x *drpcSpace_StreamStream) Send(m *ObjectSyncMessage) error {
return x.MsgSend(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{})
}
func (x *drpcSpace_StreamStream) Recv() (*ObjectSyncMessage, error) {
m := new(ObjectSyncMessage)
if err := x.MsgRecv(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{}); err != nil {
return nil, err
}
return m, nil
}
func (x *drpcSpace_StreamStream) RecvMsg(m *ObjectSyncMessage) error {
return x.MsgRecv(m, drpcEncoding_File_common_commonspace_spacesyncproto_protos_spacesync_proto{})
}

View File

@ -0,0 +1,32 @@
package storage
import (
"errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
)
const CName = "commonspace.storage"
var ErrSpaceStorageExists = errors.New("space storage exists")
type SpaceStorage interface {
storage.Provider
ACLStorage() (storage.ListStorage, error)
SpaceHeader() (*spacesyncproto.SpaceHeader, error)
StoredIds() ([]string, error)
}
type SpaceStorageCreatePayload struct {
RecWithId *aclrecordproto.RawACLRecordWithId
SpaceHeader *spacesyncproto.SpaceHeader
Id string
}
type SpaceStorageProvider interface {
app.Component
SpaceStorage(id string) (SpaceStorage, error)
CreateSpaceStorage(payload SpaceStorageCreatePayload) (SpaceStorage, error)
}

View File

@ -0,0 +1,239 @@
package syncservice
import (
"context"
"errors"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/libp2p/go-libp2p-core/sec"
"storj.io/drpc/drpcctx"
"sync"
"sync/atomic"
)
var ErrEmptyPeer = errors.New("don't have such a peer")
var ErrStreamClosed = errors.New("stream is already closed")
const maxSimultaneousOperationsPerStream = 10
// StreamPool can be made generic to work with different streams
type StreamPool interface {
SyncClient
AddAndReadStreamSync(stream spacesyncproto.SpaceStream) (err error)
AddAndReadStreamAsync(stream spacesyncproto.SpaceStream)
HasActiveStream(peerId string) bool
Close() (err error)
}
type SyncClient interface {
SendSync(peerId string, message *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error)
SendAsync(peerId string, message *spacesyncproto.ObjectSyncMessage) (err error)
BroadcastAsync(message *spacesyncproto.ObjectSyncMessage) (err error)
}
type MessageHandler func(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error)
type responseWaiter struct {
ch chan *spacesyncproto.ObjectSyncMessage
}
type streamPool struct {
sync.Mutex
peerStreams map[string]spacesyncproto.SpaceStream
messageHandler MessageHandler
wg *sync.WaitGroup
waiters map[string]responseWaiter
waitersMx sync.Mutex
counter uint64
}
func newStreamPool(messageHandler MessageHandler) StreamPool {
return &streamPool{
peerStreams: make(map[string]spacesyncproto.SpaceStream),
messageHandler: messageHandler,
wg: &sync.WaitGroup{},
}
}
func (s *streamPool) HasActiveStream(peerId string) (res bool) {
_, err := s.getOrDeleteStream(peerId)
return err == nil
}
func (s *streamPool) SendSync(
peerId string,
msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) {
newCounter := atomic.AddUint64(&s.counter, 1)
msg.TrackingId = genStreamPoolKey(peerId, msg.TreeId, newCounter)
s.waitersMx.Lock()
waiter := responseWaiter{
ch: make(chan *spacesyncproto.ObjectSyncMessage),
}
s.waiters[msg.TrackingId] = waiter
s.waitersMx.Unlock()
err = s.SendAsync(peerId, msg)
if err != nil {
return
}
reply = <-waiter.ch
return
}
func (s *streamPool) SendAsync(peerId string, message *spacesyncproto.ObjectSyncMessage) (err error) {
stream, err := s.getOrDeleteStream(peerId)
if err != nil {
return
}
return stream.Send(message)
}
func (s *streamPool) getOrDeleteStream(id string) (stream spacesyncproto.SpaceStream, err error) {
s.Lock()
defer s.Unlock()
stream, exists := s.peerStreams[id]
if !exists {
err = ErrEmptyPeer
return
}
select {
case <-stream.Context().Done():
delete(s.peerStreams, id)
err = ErrStreamClosed
default:
}
return
}
func (s *streamPool) getAllStreams() (streams []spacesyncproto.SpaceStream) {
s.Lock()
defer s.Unlock()
Loop:
for id, stream := range s.peerStreams {
select {
case <-stream.Context().Done():
delete(s.peerStreams, id)
continue Loop
default:
break
}
streams = append(streams, stream)
}
return
}
func (s *streamPool) BroadcastAsync(message *spacesyncproto.ObjectSyncMessage) (err error) {
streams := s.getAllStreams()
for _, stream := range streams {
if err = stream.Send(message); err != nil {
// TODO: add logging
}
}
return nil
}
func (s *streamPool) AddAndReadStreamAsync(stream spacesyncproto.SpaceStream) {
go s.AddAndReadStreamSync(stream)
}
func (s *streamPool) AddAndReadStreamSync(stream spacesyncproto.SpaceStream) (err error) {
s.Lock()
peerId, err := GetPeerIdFromStreamContext(stream.Context())
if err != nil {
s.Unlock()
return
}
s.peerStreams[peerId] = stream
s.wg.Add(1)
s.Unlock()
return s.readPeerLoop(peerId, stream)
}
func (s *streamPool) Close() (err error) {
s.Lock()
wg := s.wg
s.Unlock()
if wg != nil {
wg.Wait()
}
return nil
}
func (s *streamPool) readPeerLoop(peerId string, stream spacesyncproto.SpaceStream) (err error) {
defer s.wg.Done()
limiter := make(chan struct{}, maxSimultaneousOperationsPerStream)
for i := 0; i < maxSimultaneousOperationsPerStream; i++ {
limiter <- struct{}{}
}
process := func(msg *spacesyncproto.ObjectSyncMessage) {
if msg.TrackingId == "" {
s.messageHandler(stream.Context(), peerId, msg)
return
}
s.waitersMx.Lock()
waiter, exists := s.waiters[msg.TrackingId]
if !exists {
s.waitersMx.Unlock()
s.messageHandler(stream.Context(), peerId, msg)
return
}
delete(s.waiters, msg.TrackingId)
s.waitersMx.Unlock()
waiter.ch <- msg
}
Loop:
for {
msg, err := stream.Recv()
if err != nil {
break
}
select {
case <-limiter:
case <-stream.Context().Done():
break Loop
}
go func() {
process(msg)
limiter <- struct{}{}
}()
}
return s.removePeer(peerId)
}
func (s *streamPool) removePeer(peerId string) (err error) {
s.Lock()
defer s.Unlock()
_, ok := s.peerStreams[peerId]
if !ok {
return ErrEmptyPeer
}
delete(s.peerStreams, peerId)
return
}
func GetPeerIdFromStreamContext(ctx context.Context) (string, error) {
conn, ok := ctx.Value(drpcctx.TransportKey{}).(sec.SecureConn)
if !ok {
return "", fmt.Errorf("incorrect connection type in stream")
}
return conn.RemotePeer().String(), nil
}
func genStreamPoolKey(peerId, treeId string, counter uint64) string {
return fmt.Sprintf("%s.%s.%d", peerId, treeId, counter)
}

View File

@ -0,0 +1,199 @@
package syncservice
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/cache"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
)
type syncHandler struct {
spaceId string
treeCache cache.TreeCache
syncClient SyncClient
}
type SyncHandler interface {
HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error)
}
func newSyncHandler(spaceId string, treeCache cache.TreeCache, syncClient SyncClient) *syncHandler {
return &syncHandler{
spaceId: spaceId,
treeCache: treeCache,
syncClient: syncClient,
}
}
func (s *syncHandler) HandleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) error {
content := msg.GetContent()
switch {
case content.GetFullSyncRequest() != nil:
return s.HandleFullSyncRequest(ctx, senderId, content.GetFullSyncRequest(), msg)
case content.GetFullSyncResponse() != nil:
return s.HandleFullSyncResponse(ctx, senderId, content.GetFullSyncResponse(), msg)
case content.GetHeadUpdate() != nil:
return s.HandleHeadUpdate(ctx, senderId, content.GetHeadUpdate(), msg)
}
return nil
}
func (s *syncHandler) HandleHeadUpdate(
ctx context.Context,
senderId string,
update *spacesyncproto.ObjectHeadUpdate,
msg *spacesyncproto.ObjectSyncMessage) (err error) {
var (
fullRequest *spacesyncproto.ObjectFullSyncRequest
result tree.AddResult
)
res, err := s.treeCache.GetTree(ctx, s.spaceId, msg.TreeId)
if err != nil {
return
}
err = func() error {
objTree := res.TreeContainer.Tree()
objTree.Lock()
defer res.Release()
defer objTree.Unlock()
if slice.UnsortedEquals(update.Heads, objTree.Heads()) {
return nil
}
result, err = objTree.AddRawChanges(ctx, update.Changes...)
if err != nil {
return err
}
// if we couldn't add all the changes
if len(update.Changes) != len(result.Added) {
fullRequest, err = s.prepareFullSyncRequest(objTree, update)
if err != nil {
return err
}
}
return nil
}()
if fullRequest != nil {
return s.syncClient.SendAsync(senderId,
spacesyncproto.WrapFullRequest(fullRequest, msg.RootChange, msg.TreeId, msg.TrackingId))
}
return
}
func (s *syncHandler) HandleFullSyncRequest(
ctx context.Context,
senderId string,
request *spacesyncproto.ObjectFullSyncRequest,
msg *spacesyncproto.ObjectSyncMessage) (err error) {
var (
fullResponse *spacesyncproto.ObjectFullSyncResponse
header = msg.RootChange
)
defer func() {
if err != nil {
s.syncClient.SendAsync(senderId, spacesyncproto.WrapError(err, header, msg.TreeId, msg.TrackingId))
}
}()
res, err := s.treeCache.GetTree(ctx, s.spaceId, msg.TreeId)
if err != nil {
return
}
err = func() error {
objTree := res.TreeContainer.Tree()
objTree.Lock()
defer res.Release()
defer objTree.Unlock()
if header == nil {
header = objTree.Header()
}
_, err = objTree.AddRawChanges(ctx, request.Changes...)
if err != nil {
return err
}
fullResponse, err = s.prepareFullSyncResponse(request.SnapshotPath, request.Heads, objTree)
return err
}()
if err != nil {
return
}
return s.syncClient.SendAsync(senderId,
spacesyncproto.WrapFullResponse(fullResponse, header, msg.TreeId, msg.TrackingId))
}
func (s *syncHandler) HandleFullSyncResponse(
ctx context.Context,
senderId string,
response *spacesyncproto.ObjectFullSyncResponse,
msg *spacesyncproto.ObjectSyncMessage) (err error) {
res, err := s.treeCache.GetTree(ctx, s.spaceId, msg.TreeId)
if err != nil {
return
}
err = func() error {
objTree := res.TreeContainer.Tree()
objTree.Lock()
defer res.Release()
defer objTree.Unlock()
// if we already have the heads for whatever reason
if slice.UnsortedEquals(response.Heads, objTree.Heads()) {
return nil
}
_, err = objTree.AddRawChanges(ctx, response.Changes...)
return err
}()
return
}
func (s *syncHandler) prepareFullSyncRequest(
t tree.ObjectTree,
update *spacesyncproto.ObjectHeadUpdate) (req *spacesyncproto.ObjectFullSyncRequest, err error) {
req = &spacesyncproto.ObjectFullSyncRequest{
Heads: t.Heads(),
SnapshotPath: t.SnapshotPath(),
}
if len(update.Changes) != 0 {
var changesAfterSnapshot []*treechangeproto.RawTreeChangeWithId
changesAfterSnapshot, err = t.ChangesAfterCommonSnapshot(update.SnapshotPath, update.Heads)
if err != nil {
return
}
req.Changes = changesAfterSnapshot
}
return &spacesyncproto.ObjectFullSyncRequest{
Heads: t.Heads(),
SnapshotPath: t.SnapshotPath(),
}, nil
}
func (s *syncHandler) prepareFullSyncResponse(
theirPath,
theirHeads []string,
t tree.ObjectTree) (*spacesyncproto.ObjectFullSyncResponse, error) {
ourChanges, err := t.ChangesAfterCommonSnapshot(theirPath, theirHeads)
if err != nil {
return nil, err
}
return &spacesyncproto.ObjectFullSyncResponse{
Heads: t.Heads(),
Changes: ourChanges,
SnapshotPath: t.SnapshotPath(),
}, nil
}

View File

@ -0,0 +1,136 @@
package syncservice
import (
"context"
"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/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/rpc/rpcerr"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"time"
)
var log = logger.NewNamed("syncservice").Sugar()
type SyncService interface {
NotifyHeadUpdate(
ctx context.Context,
treeId string,
root *treechangeproto.RawTreeChangeWithId,
update *spacesyncproto.ObjectHeadUpdate) (err error)
StreamPool() StreamPool
Init()
Close() (err error)
}
type HeadNotifiable interface {
UpdateHeads(id string, heads []string)
}
const respPeersStreamCheckInterval = time.Second * 10
type syncService struct {
spaceId string
syncHandler SyncHandler
streamPool StreamPool
headNotifiable HeadNotifiable
configuration nodeconf.Configuration
streamLoopCtx context.Context
stopStreamLoop context.CancelFunc
streamLoopDone chan struct{}
}
func NewSyncService(spaceId string, headNotifiable HeadNotifiable, cache cache.TreeCache, configuration nodeconf.Configuration) SyncService {
var syncHandler SyncHandler
streamPool := newStreamPool(func(ctx context.Context, senderId string, message *spacesyncproto.ObjectSyncMessage) (err error) {
return syncHandler.HandleMessage(ctx, senderId, message)
})
syncHandler = newSyncHandler(spaceId, cache, streamPool)
return newSyncService(spaceId, headNotifiable, syncHandler, streamPool, configuration)
}
func newSyncService(
spaceId string,
headNotifiable HeadNotifiable,
syncHandler SyncHandler,
streamPool StreamPool,
configuration nodeconf.Configuration) *syncService {
return &syncService{
syncHandler: syncHandler,
streamPool: streamPool,
headNotifiable: headNotifiable,
configuration: configuration,
spaceId: spaceId,
streamLoopDone: make(chan struct{}),
}
}
func (s *syncService) Init() {
s.streamLoopCtx, s.stopStreamLoop = context.WithCancel(context.Background())
go s.responsibleStreamCheckLoop(s.streamLoopCtx)
}
func (s *syncService) Close() (err error) {
s.stopStreamLoop()
<-s.streamLoopDone
return s.streamPool.Close()
}
func (s *syncService) NotifyHeadUpdate(
ctx context.Context,
treeId string,
header *treechangeproto.RawTreeChangeWithId,
update *spacesyncproto.ObjectHeadUpdate) (err error) {
s.headNotifiable.UpdateHeads(treeId, update.Heads)
return s.streamPool.BroadcastAsync(spacesyncproto.WrapHeadUpdate(update, header, treeId, ""))
}
func (s *syncService) responsibleStreamCheckLoop(ctx context.Context) {
defer close(s.streamLoopDone)
checkResponsiblePeers := func() {
respPeers, err := s.configuration.ResponsiblePeers(ctx, s.spaceId)
if err != nil {
return
}
for _, peer := range respPeers {
if s.streamPool.HasActiveStream(peer.Id()) {
continue
}
cl := spacesyncproto.NewDRPCSpaceClient(peer)
stream, err := cl.Stream(ctx)
if err != nil {
// so here probably the request is failed because there is no such space,
// but diffService should handle such cases by sending pushSpace
continue
}
// sending empty message for the server to understand from which space is it coming
err = stream.Send(&spacesyncproto.ObjectSyncMessage{SpaceId: s.spaceId})
if err != nil {
err = rpcerr.Unwrap(err)
log.With("spaceId", s.spaceId).Errorf("failed to open stream: %v", err)
continue
}
s.streamPool.AddAndReadStreamAsync(stream)
}
}
checkResponsiblePeers()
ticker := time.NewTicker(respPeersStreamCheckInterval)
defer ticker.Stop()
for {
select {
case <-s.streamLoopCtx.Done():
return
case <-ticker.C:
checkResponsiblePeers()
}
}
}
func (s *syncService) StreamPool() StreamPool {
return s.streamPool
}

View File

@ -0,0 +1,142 @@
package synctree
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
)
type UpdateListener interface {
Update(tree tree.ObjectTree)
Rebuild(tree tree.ObjectTree)
}
// SyncTree sends head updates to sync service and also sends new changes to update listener
type SyncTree struct {
tree.ObjectTree
syncService syncservice.SyncService
listener UpdateListener
}
func DeriveSyncTree(
ctx context.Context,
payload tree.ObjectTreeCreatePayload,
syncService syncservice.SyncService,
listener UpdateListener,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (t tree.ObjectTree, err error) {
t, err = tree.CreateDerivedObjectTree(payload, aclList, createStorage)
if err != nil {
return
}
t = &SyncTree{
ObjectTree: t,
syncService: syncService,
listener: listener,
}
err = syncService.NotifyHeadUpdate(ctx, t.ID(), t.Header(), &spacesyncproto.ObjectHeadUpdate{
Heads: t.Heads(),
SnapshotPath: t.SnapshotPath(),
})
return
}
func CreateSyncTree(
ctx context.Context,
payload tree.ObjectTreeCreatePayload,
syncService syncservice.SyncService,
listener UpdateListener,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (t tree.ObjectTree, err error) {
t, err = tree.CreateObjectTree(payload, aclList, createStorage)
if err != nil {
return
}
t = &SyncTree{
ObjectTree: t,
syncService: syncService,
listener: listener,
}
err = syncService.NotifyHeadUpdate(ctx, t.ID(), t.Header(), &spacesyncproto.ObjectHeadUpdate{
Heads: t.Heads(),
SnapshotPath: t.SnapshotPath(),
})
return
}
func BuildSyncTree(
ctx context.Context,
syncService syncservice.SyncService,
treeStorage storage.TreeStorage,
listener UpdateListener,
aclList list.ACLList) (t tree.ObjectTree, err error) {
return buildSyncTree(ctx, syncService, treeStorage, listener, aclList)
}
func buildSyncTree(
ctx context.Context,
syncService syncservice.SyncService,
treeStorage storage.TreeStorage,
listener UpdateListener,
aclList list.ACLList) (t tree.ObjectTree, err error) {
t, err = tree.BuildObjectTree(treeStorage, aclList)
if err != nil {
return
}
t = &SyncTree{
ObjectTree: t,
syncService: syncService,
listener: listener,
}
err = syncService.NotifyHeadUpdate(ctx, t.ID(), t.Header(), &spacesyncproto.ObjectHeadUpdate{
Heads: t.Heads(),
SnapshotPath: t.SnapshotPath(),
})
return
}
func (s *SyncTree) AddContent(ctx context.Context, content tree.SignableChangeContent) (res tree.AddResult, err error) {
res, err = s.AddContent(ctx, content)
if err != nil {
return
}
err = s.syncService.NotifyHeadUpdate(ctx, s.ID(), s.Header(), &spacesyncproto.ObjectHeadUpdate{
Heads: res.Heads,
Changes: res.Added,
SnapshotPath: s.SnapshotPath(),
})
return
}
func (s *SyncTree) AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (res tree.AddResult, err error) {
res, err = s.AddRawChanges(ctx, changes...)
if err != nil {
return
}
switch res.Mode {
case tree.Nothing:
return
case tree.Append:
s.listener.Update(s)
case tree.Rebuild:
s.listener.Rebuild(s)
}
err = s.syncService.NotifyHeadUpdate(ctx, s.ID(), s.Header(), &spacesyncproto.ObjectHeadUpdate{
Heads: res.Heads,
Changes: res.Added,
SnapshotPath: s.SnapshotPath(),
})
return
}
func (s *SyncTree) Tree() tree.ObjectTree {
return s
}

View File

@ -5,10 +5,9 @@ import (
"errors" "errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/secure"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config" "github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/rpc"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/secure"
"github.com/libp2p/go-libp2p-core/sec" "github.com/libp2p/go-libp2p-core/sec"
"go.uber.org/zap" "go.uber.org/zap"
"net" "net"
@ -40,7 +39,7 @@ type dialer struct {
mu sync.RWMutex mu sync.RWMutex
} }
func (d *dialer) Init(ctx context.Context, a *app.App) (err error) { func (d *dialer) Init(a *app.App) (err error) {
d.transport = a.MustComponent(secure.CName).(secure.Service) d.transport = a.MustComponent(secure.CName).(secure.Service)
nodes := a.MustComponent(config.CName).(*config.Config).Nodes nodes := a.MustComponent(config.CName).(*config.Config).Nodes
d.peerAddrs = map[string][]string{} d.peerAddrs = map[string][]string{}
@ -60,7 +59,7 @@ func (d *dialer) UpdateAddrs(addrs map[string][]string) {
d.mu.Unlock() d.mu.Unlock()
} }
func (d *dialer) Dial(ctx context.Context, peerId string) (peer peer.Peer, err error) { func (d *dialer) Dial(ctx context.Context, peerId string) (p peer.Peer, err error) {
d.mu.RLock() d.mu.RLock()
defer d.mu.RUnlock() defer d.mu.RUnlock()
addrs, ok := d.peerAddrs[peerId] addrs, ok := d.peerAddrs[peerId]
@ -68,11 +67,11 @@ func (d *dialer) Dial(ctx context.Context, peerId string) (peer peer.Peer, err e
return nil, ErrArrdsNotFound return nil, ErrArrdsNotFound
} }
var ( var (
stream drpc.Stream conn drpc.Conn
sc sec.SecureConn sc sec.SecureConn
) )
for _, addr := range addrs { for _, addr := range addrs {
stream, sc, err = d.makeStream(ctx, addr) conn, sc, err = d.handshake(ctx, addr)
if err != nil { if err != nil {
log.Info("can't connect to host", zap.String("addr", addr)) log.Info("can't connect to host", zap.String("addr", addr))
} else { } else {
@ -83,10 +82,10 @@ func (d *dialer) Dial(ctx context.Context, peerId string) (peer peer.Peer, err e
if err != nil { if err != nil {
return return
} }
return rpc.PeerFromStream(sc, stream, false), nil return peer.NewPeer(sc, conn), nil
} }
func (d *dialer) makeStream(ctx context.Context, addr string) (stream drpc.Stream, sc sec.SecureConn, err error) { func (d *dialer) handshake(ctx context.Context, addr string) (conn drpc.Conn, sc sec.SecureConn, err error) {
tcpConn, err := net.Dial("tcp", addr) tcpConn, err := net.Dial("tcp", addr)
if err != nil { if err != nil {
return return
@ -96,9 +95,6 @@ func (d *dialer) makeStream(ctx context.Context, addr string) (stream drpc.Strea
return return
} }
log.Info("connected with remote host", zap.String("serverPeer", sc.RemotePeer().String()), zap.String("per", sc.LocalPeer().String())) log.Info("connected with remote host", zap.String("serverPeer", sc.RemotePeer().String()), zap.String("per", sc.LocalPeer().String()))
stream, err = drpcconn.New(sc).NewStream(ctx, "", rpc.Encoding) conn = drpcconn.New(sc)
if err != nil { return conn, sc, err
return
}
return stream, sc, err
} }

59
common/net/peer/peer.go Normal file
View File

@ -0,0 +1,59 @@
package peer
import (
"context"
"github.com/libp2p/go-libp2p-core/sec"
"storj.io/drpc"
"sync/atomic"
"time"
)
func NewPeer(sc sec.SecureConn, conn drpc.Conn) Peer {
return &peer{
id: sc.RemotePeer().String(),
lastUsage: time.Now().Unix(),
sc: sc,
Conn: conn,
}
}
type Peer interface {
Id() string
LastUsage() time.Time
UpdateLastUsage()
drpc.Conn
}
type peer struct {
id string
lastUsage int64
sc sec.SecureConn
drpc.Conn
}
func (p *peer) Id() string {
return p.id
}
func (p *peer) LastUsage() time.Time {
select {
case <-p.Closed():
return time.Unix(0, 0)
default:
}
return time.Unix(atomic.LoadInt64(&p.lastUsage), 0)
}
func (p *peer) Invoke(ctx context.Context, rpc string, enc drpc.Encoding, in, out drpc.Message) error {
defer p.UpdateLastUsage()
return p.Conn.Invoke(ctx, rpc, enc, in, out)
}
func (p *peer) NewStream(ctx context.Context, rpc string, enc drpc.Encoding) (drpc.Stream, error) {
defer p.UpdateLastUsage()
return p.Conn.NewStream(ctx, rpc, enc)
}
func (p *peer) UpdateLastUsage() {
atomic.StoreInt64(&p.lastUsage, time.Now().Unix())
}

107
common/net/pool/pool.go Normal file
View File

@ -0,0 +1,107 @@
package pool
import (
"context"
"errors"
"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/net/dialer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache"
"math/rand"
"time"
)
const (
CName = "net.pool"
)
var log = logger.NewNamed(CName)
var (
ErrUnableToConnect = errors.New("unable to connect")
)
func New() Pool {
return &pool{}
}
// Pool creates and caches outgoing connection
type Pool interface {
// Get lookups to peer in existing connections or creates and cache new one
Get(ctx context.Context, id string) (peer.Peer, error)
// GetOneOf searches at least one existing connection in cache or creates a new one from a randomly selected id from given list
GetOneOf(ctx context.Context, peerIds []string) (peer.Peer, error)
app.ComponentRunnable
}
type pool struct {
cache ocache.OCache
}
func (p *pool) Init(a *app.App) (err error) {
dialer := a.MustComponent(dialer.CName).(dialer.Dialer)
p.cache = ocache.New(
func(ctx context.Context, id string) (value ocache.Object, err error) {
return dialer.Dial(ctx, id)
},
ocache.WithLogger(log.Sugar()),
ocache.WithGCPeriod(time.Minute),
ocache.WithTTL(time.Minute*5),
ocache.WithRefCounter(false),
)
return nil
}
func (p *pool) Name() (name string) {
return CName
}
func (p *pool) Run(ctx context.Context) (err error) {
return nil
}
func (p *pool) Get(ctx context.Context, id string) (peer.Peer, error) {
v, err := p.cache.Get(ctx, id)
if err != nil {
return nil, err
}
pr := v.(peer.Peer)
select {
case <-pr.Closed():
default:
return pr, nil
}
p.cache.Remove(id)
return p.Get(ctx, id)
}
func (p *pool) GetOneOf(ctx context.Context, peerIds []string) (peer.Peer, error) {
// finding existing connection
for _, peerId := range peerIds {
if v, err := p.cache.Pick(ctx, peerId); err == nil {
pr := v.(peer.Peer)
select {
case <-pr.Closed():
default:
return pr, nil
}
}
}
// shuffle ids for better consistency
rand.Shuffle(len(peerIds), func(i, j int) {
peerIds[i], peerIds[j] = peerIds[j], peerIds[i]
})
// connecting
for _, peerId := range peerIds {
if v, err := p.cache.Get(ctx, peerId); err == nil {
return v.(peer.Peer), nil
}
}
return nil, ErrUnableToConnect
}
func (p *pool) Close(ctx context.Context) (err error) {
return p.cache.Close()
}

View File

@ -0,0 +1,213 @@
package pool
import (
"context"
"errors"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/dialer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/drpc"
"testing"
"time"
)
var ctx = context.Background()
func TestPool_Get(t *testing.T) {
t.Run("dial error", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
var expErr = errors.New("dial error")
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return nil, expErr
}
p, err := fx.Get(ctx, "1")
assert.Nil(t, p)
assert.EqualError(t, err, expErr.Error())
})
t.Run("dial and cached", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return newTestPeer("1"), nil
}
p, err := fx.Get(ctx, "1")
assert.NoError(t, err)
assert.NotNil(t, p)
fx.Dialer.dial = nil
p, err = fx.Get(ctx, "1")
assert.NoError(t, err)
assert.NotNil(t, p)
})
t.Run("retry for closed", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
tp := newTestPeer("1")
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return tp, nil
}
p, err := fx.Get(ctx, "1")
assert.NoError(t, err)
assert.NotNil(t, p)
p.Close()
tp2 := newTestPeer("1")
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return tp2, nil
}
p, err = fx.Get(ctx, "1")
assert.NoError(t, err)
assert.Equal(t, p, tp2)
})
}
func TestPool_GetOneOf(t *testing.T) {
addToCache := func(t *testing.T, fx *fixture, tp *testPeer) {
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return tp, nil
}
gp, err := fx.Get(ctx, tp.Id())
require.NoError(t, err)
require.Equal(t, gp, tp)
}
t.Run("from cache", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
tp1 := newTestPeer("1")
addToCache(t, fx, tp1)
p, err := fx.GetOneOf(ctx, []string{"3", "2", "1"})
require.NoError(t, err)
assert.Equal(t, tp1, p)
})
t.Run("from cache - skip closed", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
tp2 := newTestPeer("2")
addToCache(t, fx, tp2)
tp2.Close()
tp1 := newTestPeer("1")
addToCache(t, fx, tp1)
p, err := fx.GetOneOf(ctx, []string{"3", "2", "1"})
require.NoError(t, err)
assert.Equal(t, tp1, p)
})
t.Run("dial", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
var called bool
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
if called {
return nil, fmt.Errorf("not expected call")
}
called = true
return newTestPeer(peerId), nil
}
p, err := fx.GetOneOf(ctx, []string{"3", "2", "1"})
require.NoError(t, err)
assert.NotNil(t, p)
})
t.Run("unable to connect", func(t *testing.T) {
fx := newFixture(t)
defer fx.Finish()
fx.Dialer.dial = func(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return nil, fmt.Errorf("persistent error")
}
p, err := fx.GetOneOf(ctx, []string{"3", "2", "1"})
assert.Equal(t, ErrUnableToConnect, err)
assert.Nil(t, p)
})
}
func newFixture(t *testing.T) *fixture {
fx := &fixture{
Pool: New(),
Dialer: &dialerMock{},
}
a := new(app.App)
a.Register(fx.Pool)
a.Register(fx.Dialer)
require.NoError(t, a.Start(context.Background()))
fx.a = a
fx.t = t
return fx
}
func (fx *fixture) Finish() {
require.NoError(fx.t, fx.a.Close(context.Background()))
}
type fixture struct {
Pool
Dialer *dialerMock
a *app.App
t *testing.T
}
var _ dialer.Dialer = (*dialerMock)(nil)
type dialerMock struct {
dial func(ctx context.Context, peerId string) (peer peer.Peer, err error)
}
func (d *dialerMock) Dial(ctx context.Context, peerId string) (peer peer.Peer, err error) {
return d.dial(ctx, peerId)
}
func (d *dialerMock) UpdateAddrs(addrs map[string][]string) {
return
}
func (d *dialerMock) Init(a *app.App) (err error) {
return
}
func (d *dialerMock) Name() (name string) {
return dialer.CName
}
func newTestPeer(id string) *testPeer {
return &testPeer{
id: id,
closed: make(chan struct{}),
}
}
type testPeer struct {
id string
closed chan struct{}
}
func (t *testPeer) Id() string {
return t.id
}
func (t *testPeer) LastUsage() time.Time {
return time.Now()
}
func (t *testPeer) UpdateLastUsage() {}
func (t *testPeer) Close() error {
select {
case <-t.closed:
return fmt.Errorf("already closed")
default:
close(t.closed)
}
return nil
}
func (t *testPeer) Closed() <-chan struct{} {
return t.closed
}
func (t *testPeer) Invoke(ctx context.Context, rpc string, enc drpc.Encoding, in, out drpc.Message) error {
return fmt.Errorf("call Invoke on test peer")
}
func (t *testPeer) NewStream(ctx context.Context, rpc string, enc drpc.Encoding) (drpc.Stream, error) {
return nil, fmt.Errorf("call NewStream on test peer")
}

View File

@ -0,0 +1,51 @@
package rpcerr
import (
"errors"
"fmt"
"storj.io/drpc/drpcerr"
)
var (
Unexpected = RegisterErr(errors.New("unexpected"), 1)
Closed = RegisterErr(errors.New("closed"), 2)
)
var (
errsMap = make(map[uint64]error)
)
func RegisterErr(err error, code uint64) error {
if e, ok := errsMap[code]; ok {
panic(fmt.Errorf("attempt to register error with exiswting code: %d; registered error: %v", code, e))
}
errWithCode := drpcerr.WithCode(err, code)
errsMap[code] = errWithCode
return errWithCode
}
func Err(code uint64) error {
err, ok := errsMap[code]
if !ok {
return drpcerr.WithCode(fmt.Errorf("unexpected error, code: %d", code), code)
}
return err
}
func Unwrap(e error) error {
code := drpcerr.Code(e)
if code == 0 {
return e
}
err, ok := errsMap[code]
if !ok {
return drpcerr.WithCode(fmt.Errorf("unexpected error: %v; code: %d", err, code), code)
}
return err
}
type ErrGroup int64
func (g ErrGroup) Register(err error, code uint64) error {
return RegisterErr(err, uint64(g)+code)
}

View File

@ -4,15 +4,16 @@ import (
"context" "context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
secure2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/secure"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config" "github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool" "github.com/zeebo/errs"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/rpc"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/secure"
"go.uber.org/zap" "go.uber.org/zap"
"io"
"net" "net"
"storj.io/drpc" "storj.io/drpc"
"storj.io/drpc/drpcmux"
"storj.io/drpc/drpcserver" "storj.io/drpc/drpcserver"
"strings"
"time" "time"
) )
@ -21,25 +22,27 @@ const CName = "net/drpcserver"
var log = logger.NewNamed(CName) var log = logger.NewNamed(CName)
func New() DRPCServer { func New() DRPCServer {
return &drpcServer{} return &drpcServer{Mux: drpcmux.New()}
} }
type DRPCServer interface { type DRPCServer interface {
app.ComponentRunnable app.ComponentRunnable
drpc.Mux
} }
type drpcServer struct { type drpcServer struct {
config config.GrpcServer config config.GrpcServer
drpcServer *drpcserver.Server drpcServer *drpcserver.Server
transport secure.Service transport secure2.Service
listeners []secure.ContextListener listeners []secure2.ContextListener
pool pool.Pool pool pool.Pool
cancel func() cancel func()
*drpcmux.Mux
} }
func (s *drpcServer) Init(ctx context.Context, a *app.App) (err error) { func (s *drpcServer) Init(a *app.App) (err error) {
s.config = a.MustComponent(config.CName).(*config.Config).GrpcServer s.config = a.MustComponent(config.CName).(*config.Config).GrpcServer
s.transport = a.MustComponent(secure.CName).(secure.Service) s.transport = a.MustComponent(secure2.CName).(secure2.Service)
s.pool = a.MustComponent(pool.CName).(pool.Pool) s.pool = a.MustComponent(pool.CName).(pool.Pool)
return nil return nil
} }
@ -49,7 +52,7 @@ func (s *drpcServer) Name() (name string) {
} }
func (s *drpcServer) Run(ctx context.Context) (err error) { func (s *drpcServer) Run(ctx context.Context) (err error) {
s.drpcServer = drpcserver.New(s) s.drpcServer = drpcserver.New(s.Mux)
ctx, s.cancel = context.WithCancel(ctx) ctx, s.cancel = context.WithCancel(ctx)
for _, addr := range s.config.ListenAddrs { for _, addr := range s.config.ListenAddrs {
tcpList, err := net.Listen("tcp", addr) tcpList, err := net.Listen("tcp", addr)
@ -62,7 +65,7 @@ func (s *drpcServer) Run(ctx context.Context) (err error) {
return return
} }
func (s *drpcServer) serve(ctx context.Context, lis secure.ContextListener) { func (s *drpcServer) serve(ctx context.Context, lis secure2.ContextListener) {
l := log.With(zap.String("localAddr", lis.Addr().String())) l := log.With(zap.String("localAddr", lis.Addr().String()))
l.Info("drpc listener started") l.Info("drpc listener started")
defer func() { defer func() {
@ -86,7 +89,7 @@ func (s *drpcServer) serve(ctx context.Context, lis secure.ContextListener) {
} }
continue continue
} }
if _, ok := err.(secure.HandshakeError); ok { if _, ok := err.(secure2.HandshakeError); ok {
l.Warn("listener handshake error", zap.Error(err)) l.Warn("listener handshake error", zap.Error(err))
continue continue
} }
@ -101,7 +104,7 @@ func (s *drpcServer) serveConn(ctx context.Context, conn net.Conn) {
l := log.With(zap.String("remoteAddr", conn.RemoteAddr().String())).With(zap.String("localAddr", conn.LocalAddr().String())) l := log.With(zap.String("remoteAddr", conn.RemoteAddr().String())).With(zap.String("localAddr", conn.LocalAddr().String()))
l.Debug("connection opened") l.Debug("connection opened")
if err := s.drpcServer.ServeOne(ctx, conn); err != nil { if err := s.drpcServer.ServeOne(ctx, conn); err != nil {
if err == context.Canceled || strings.Contains(err.Error(), "EOF") { if errs.Is(err, context.Canceled) || errs.Is(err, io.EOF) {
l.Debug("connection closed") l.Debug("connection closed")
} else { } else {
l.Warn("serve connection error", zap.Error(err)) l.Warn("serve connection error", zap.Error(err))
@ -109,16 +112,6 @@ func (s *drpcServer) serveConn(ctx context.Context, conn net.Conn) {
} }
} }
func (s *drpcServer) HandleRPC(stream drpc.Stream, _ string) (err error) {
ctx := stream.Context()
sc, err := secure.CtxSecureConn(ctx)
if err != nil {
return
}
log.With(zap.String("peer", sc.RemotePeer().String())).Debug("stream opened")
return s.pool.AddAndReadPeer(rpc.PeerFromStream(sc, stream, true))
}
func (s *drpcServer) Close(ctx context.Context) (err error) { func (s *drpcServer) Close(ctx context.Context) (err error) {
if s.cancel != nil { if s.cancel != nil {
s.cancel() s.cancel()

View File

@ -2,13 +2,11 @@ package secure
import ( import (
"context" "context"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config" "github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/sec" "github.com/libp2p/go-libp2p-core/sec"
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls" libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
"go.uber.org/zap" "go.uber.org/zap"
@ -35,10 +33,9 @@ type service struct {
key crypto.PrivKey key crypto.PrivKey
} }
func (s *service) Init(ctx context.Context, a *app.App) (err error) { func (s *service) Init(a *app.App) (err error) {
account := a.MustComponent(config.CName).(*config.Config).Account account := a.MustComponent(config.CName).(*config.Config).Account
decoder := signingkey.NewEDPrivKeyDecoder() pkb, err := keys.DecodeBytesFromString(account.SigningKey)
pkb, err := decoder.DecodeFromStringIntoBytes(account.SigningKey)
if err != nil { if err != nil {
return return
} }
@ -46,28 +43,6 @@ func (s *service) Init(ctx context.Context, a *app.App) (err error) {
return return
} }
pid, err := peer.Decode(account.PeerId)
if err != nil {
return
}
var testData = []byte("test data")
sign, err := s.key.Sign(testData)
if err != nil {
return
}
pubKey, err := pid.ExtractPublicKey()
if err != nil {
return
}
ok, err := pubKey.Verify(testData, sign)
if err != nil {
return
}
if !ok {
return fmt.Errorf("peerId and privateKey mismatched")
}
log.Info("secure service init", zap.String("peerId", account.PeerId)) log.Info("secure service init", zap.String("peerId", account.PeerId))
return nil return nil

View File

@ -1,10 +1,10 @@
package configuration package nodeconf
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/peer" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/peer"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool" "github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
"github.com/anytypeio/go-chash" "github.com/anytypeio/go-chash"
) )
@ -13,12 +13,14 @@ func New() Service {
} }
type Configuration interface { type Configuration interface {
// Id returns current configuration id // Id returns current nodeconf id
Id() string Id() string
// AllPeers returns all peers by spaceId except current account // AllPeers returns all peers by spaceId except current account
AllPeers(ctx context.Context, spaceId string) (peers []peer.Peer, err error) AllPeers(ctx context.Context, spaceId string) (peers []peer.Peer, err error)
// OnePeer returns one of peer for spaceId // OnePeer returns one of peer for spaceId
OnePeer(ctx context.Context, spaceId string) (p peer.Peer, err error) OnePeer(ctx context.Context, spaceId string) (p peer.Peer, err error)
// ResponsiblePeers returns peers for the space id that are responsible for the space
ResponsiblePeers(ctx context.Context, spaceId string) (peers []peer.Peer, err error)
// NodeIds returns list of peerId for given spaceId // NodeIds returns list of peerId for given spaceId
NodeIds(spaceId string) []string NodeIds(spaceId string) []string
// IsResponsible checks if current account responsible for given spaceId // IsResponsible checks if current account responsible for given spaceId
@ -40,7 +42,7 @@ func (c *configuration) AllPeers(ctx context.Context, spaceId string) (peers []p
nodeIds := c.NodeIds(spaceId) nodeIds := c.NodeIds(spaceId)
peers = make([]peer.Peer, 0, len(nodeIds)) peers = make([]peer.Peer, 0, len(nodeIds))
for _, id := range nodeIds { for _, id := range nodeIds {
p, e := c.pool.DialAndAddPeer(ctx, id) p, e := c.pool.Get(ctx, id)
if e == nil { if e == nil {
peers = append(peers, p) peers = append(peers, p)
} }
@ -51,9 +53,23 @@ func (c *configuration) AllPeers(ctx context.Context, spaceId string) (peers []p
return return
} }
func (c *configuration) ResponsiblePeers(ctx context.Context, spaceId string) (peers []peer.Peer, err error) {
if c.IsResponsible(spaceId) {
return c.AllPeers(ctx, spaceId)
} else {
var one peer.Peer
one, err = c.OnePeer(ctx, spaceId)
if err != nil {
return
}
peers = []peer.Peer{one}
return
}
}
func (c *configuration) OnePeer(ctx context.Context, spaceId string) (p peer.Peer, err error) { func (c *configuration) OnePeer(ctx context.Context, spaceId string) (p peer.Peer, err error) {
nodeIds := c.NodeIds(spaceId) nodeIds := c.NodeIds(spaceId)
return c.pool.GetOrDialOneOf(ctx, nodeIds) return c.pool.GetOneOf(ctx, nodeIds)
} }
func (c *configuration) NodeIds(spaceId string) []string { func (c *configuration) NodeIds(spaceId string) []string {

124
common/nodeconf/service.go Normal file
View File

@ -0,0 +1,124 @@
package nodeconf
import (
"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/net/pool"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-chash"
)
const CName = "common.nodeconf"
const (
partitionCount = 3000
replicationFactor = 3
)
var log = logger.NewNamed(CName)
type Service interface {
GetLast() Configuration
GetById(id string) Configuration
app.Component
}
type service struct {
accountId string
pool pool.Pool
last Configuration
}
type Node struct {
Address string
PeerId string
SigningKey signingkey.PubKey
EncryptionKey encryptionkey.PubKey
}
func (n *Node) Id() string {
return n.PeerId
}
func (n *Node) Capacity() float64 {
return 1
}
func (s *service) Init(a *app.App) (err error) {
conf := a.MustComponent(config.CName).(*config.Config)
s.accountId = conf.Account.PeerId
s.pool = a.MustComponent(pool.CName).(pool.Pool)
config := &configuration{
id: "config",
accountId: s.accountId,
pool: s.pool,
}
if config.chash, err = chash.New(chash.Config{
PartitionCount: partitionCount,
ReplicationFactor: replicationFactor,
}); err != nil {
return
}
members := make([]chash.Member, 0, len(conf.Nodes)-1)
for _, n := range conf.Nodes {
if n.PeerId == conf.Account.PeerId {
continue
}
var member *Node
member, err = nodeFromConfigNode(n)
if err != nil {
return
}
members = append(members, member)
}
if err = config.chash.AddMembers(members...); err != nil {
return
}
s.last = config
return
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) GetLast() Configuration {
return s.last
}
func (s *service) GetById(id string) Configuration {
//TODO implement me
panic("implement me")
}
func nodeFromConfigNode(
n config.Node) (*Node, error) {
decodedSigningKey, err := keys.DecodeKeyFromString(
n.SigningKey,
signingkey.UnmarshalEd25519PrivateKey,
nil)
if err != nil {
return nil, err
}
decodedEncryptionKey, err := keys.DecodeKeyFromString(
n.SigningKey,
encryptionkey.NewEncryptionRsaPrivKeyFromBytes,
nil)
if err != nil {
return nil, err
}
return &Node{
Address: n.Address,
PeerId: n.PeerId,
SigningKey: decodedSigningKey.GetPublic(),
EncryptionKey: decodedEncryptionKey.GetPublic(),
}, nil
}

View File

@ -1,7 +1,6 @@
package config package config
import ( import (
"context"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app" "github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
@ -32,8 +31,8 @@ type Config struct {
Space Space `yaml:"space"` Space Space `yaml:"space"`
} }
func (c *Config) Init(ctx context.Context, a *app.App) (err error) { func (c *Config) Init(a *app.App) (err error) {
logger.NewNamed("config").Info(fmt.Sprint(*c)) logger.NewNamed("config").Info(fmt.Sprint(c.Space))
return return
} }

View File

@ -1,6 +1,6 @@
package config package config
type Space struct { type Space struct {
GCTTL int `json:"gcTTL"` GCTTL int `yaml:"gcTTL"`
SyncPeriod int `json:"syncPeriod"` SyncPeriod int `yaml:"syncPeriod"`
} }

View File

@ -1,16 +0,0 @@
anytype:
swarmKey: "/key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec"
grpcServer:
listenAddrs:
- "127.0.0.1:4431"
peerList:
myId:
peerId: "12D3KooWA4FLWvrMbCtp2MbzKcC5RRN7HqxxBxPcSADFfzrGiW3U"
privKey: "InCGjb55V9+jj2PebUExUuwrpOIBc4hmgk2dSqyk3k4DjmgrdoNVuFe7xCFaFdUVb0RJYj6A+OTp2yXASTmq2w=="
remote:
- peerId: "12D3KooWHJpSEMQUZCyK8TK181LhjzntWjKfXDr7MWks9cw41R2C"
addr: "127.0.0.1:4430"
- peerId: "12D3KooWK6c1CPLL4Bvjim9A9SDRmehy12hYjbqX1VASHKfH7W7H"
addr: "127.0.0.1:4432"

View File

@ -1,16 +0,0 @@
anytype:
swarmKey: "/key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec"
grpcServer:
listenAddrs:
- "127.0.0.1:4432"
peerList:
myId:
peerId: "12D3KooWK6c1CPLL4Bvjim9A9SDRmehy12hYjbqX1VASHKfH7W7H"
privKey: "jynYZBgtM4elT+6e7M5UERTJCZgUd3hDdmQjCqTpApyJ4h53V6TQan4Ru4OXqz+91rCLjpIVdphhaB0l+TvNsA=="
remote:
- peerId: "12D3KooWA4FLWvrMbCtp2MbzKcC5RRN7HqxxBxPcSADFfzrGiW3U"
addr: "127.0.0.1:4431"
- peerId: "12D3KooWHJpSEMQUZCyK8TK181LhjzntWjKfXDr7MWks9cw41R2C"
addr: "127.0.0.1:4430"

View File

@ -3,20 +3,22 @@ anytype:
grpcServer: grpcServer:
listenAddrs: listenAddrs:
- 127.0.0.1:4430 - 127.0.0.1:4430
- 127.0.0.1:4431
tls: false tls: false
account: account:
peerId: 12D3KooWMHuhZgK2skkLrvL51QQTXaXQKYy2QqfvPNBFnzR2ubA1 peerId: 12D3KooWSUx2LXPvoZGp72Dt7b7r1kPSmQ6zAUwKkHFyX64uiXRu
signingKey: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug signingKey: 4QTrtkFLQe9wQcWT/cgFEwfMHB5pt4axInNmCIMCZaz3nVdyygRoO8/YH0V15X6Mnw1NQWsS1YIWiLS22hwepA==
encryptionKey: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd encryptionKey: MIIEpAIBAAKCAQEAmqAAOPfR86po3m+zwSzbAlZGgMMF188v35Ulqf9Gb4KO8DZ9ifxrqpjlLZRxPKeXj3wSSQEXsJf3A82rZlDxddZSM0i7Mx5G2G0zRHWx9dC58PpX6o/fDuvSwcyXqOgIK55N/hyEuIbWQgp5Rk9uy2Zbrhv5ZL5CvceM0b9wSKt/hRvntxSbG+HRgXWaQvAReGuJrySVvkh6fhC3G0IwqyFbGNq2zqAJej6NBzZA3thHgTn5PoWD8O4cyukBxunKGu3HLE3vJtqEMFrkNFw5SMpdEtxyTLN6T1HIeYCY9RL+BFYfxIWg6pGtIoIJKUB0XapJr9ltzvXfT9KeSCU0VwIDAQABAoIBAAp/xsQXf7gN4CUKbKg3RX+5H/xqQaFPvi5uUCpk3QGBWfdRm+CctSrWSul3ZOD7eD0T7aHrYxJonysw8ex2no6jyN0WmS91ZNYZRBvn6feI/rcwKHwS3NCEjsD+BWZAqx1bGGyivxhQf4fociemCR3ii2MdHygKCzobrKIpX5RvhanI4j01dyLlxwqTsteuc/o5RR4jfg1eN0kldFjk3UcSNyzzEv5o5UhRsHCLJBTNTvYZBN4FpyaqcLT9gKS9aVBvQH63R+E5dyxo1+24tZZricW59h2bN3CFriqkwBo1y0gTnR6VQ22MBvIUxYUm82cxXs/Vr0YQTSAaEGThxFECgYEAxKQMRnM39WMzrNx1WDwpBERRj1T0TbLf1uq6viPiLdik2Tm2aCBZyr5j82Ey7fZ7OafKGfsM0I2AuYeoBdYDuYN6A7tE9kpnECubnWuIvUeYcL+1VzzMedVtdKwQXrYbhqKtyvnSJ9gQ6CusHtsDE1bQvTMxBX4KNBeBYllCUasCgYEAyU0RPUaj56CyLHKty8jNg6wl+06IZ0pUPIWZ//b1zeZrlHGYDp/InxS8huGFapzOg1sbQBS6j3j3YE3Ts6v6FNuIa4pcPQ91YuHiWWQdgVjrCZdleanFWGTjIx12+RGj9vx4voRhNQcHW1YeTvvyj4BN/ECR6GNaoS/ZjBKo1AUCgYEAj6AyxxJJARagG9Y6b2QhoVg1Kjem6UmJbPStyUt0XIAsh+07afqXGxrM7mtEQ8MQZiBD4Y4Y4gs4xkprUzfqKIn7iNYznKDjflAbrXNpwLaWhWPBFCL4RtS4ycsTedoRaNlRjzvBYBDU6H9djHvzVyDF/itx1s0krr+sZSVE51kCgYBxGRinecna+KFCccgNp6s34H+Se2QNzGgZfOKyOjmOTniA9XV+Oe3I2yi1C34fESzCBm0ACuVqeIdcFz3rQ6OFFnbGHP2H3OiR/uFiYepl4uRjBimgOm9DI6Ot9f8DHxMlUGIygEPxPBq5CWCL9egpEeg+4rRXgYLI7w5mMZGjVQKBgQDC4qyH7FK3lLv5JomoK6nNjpyPNBmr0Rt215oM/AWQaxDhFZH5un68ueZ7MfybwXxHHFQ4ZeSwYs006f1XGPNW6qrH6pi/3SCLFuGVfNnLVwCBkm3QaQrxFm3v9LmVCidTNta0l0DrUldZdK8/P31GBxKo/MmYF/f9LO/Mfm/uDg==
apiServer: apiServer:
port: "8080" port: "8080"
nodes: nodes:
- peerId: 12D3KooWMHuhZgK2skkLrvL51QQTXaXQKYy2QqfvPNBFnzR2ubA1 - peerId: 12D3KooWSUx2LXPvoZGp72Dt7b7r1kPSmQ6zAUwKkHFyX64uiXRu
address: 127.0.0.1:4430 address: 127.0.0.1:4430
signingKey: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug signingKey: 4QTrtkFLQe9wQcWT/cgFEwfMHB5pt4axInNmCIMCZaz3nVdyygRoO8/YH0V15X6Mnw1NQWsS1YIWiLS22hwepA==
encryptionKey: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd encryptionKey: MIIEpAIBAAKCAQEAmqAAOPfR86po3m+zwSzbAlZGgMMF188v35Ulqf9Gb4KO8DZ9ifxrqpjlLZRxPKeXj3wSSQEXsJf3A82rZlDxddZSM0i7Mx5G2G0zRHWx9dC58PpX6o/fDuvSwcyXqOgIK55N/hyEuIbWQgp5Rk9uy2Zbrhv5ZL5CvceM0b9wSKt/hRvntxSbG+HRgXWaQvAReGuJrySVvkh6fhC3G0IwqyFbGNq2zqAJej6NBzZA3thHgTn5PoWD8O4cyukBxunKGu3HLE3vJtqEMFrkNFw5SMpdEtxyTLN6T1HIeYCY9RL+BFYfxIWg6pGtIoIJKUB0XapJr9ltzvXfT9KeSCU0VwIDAQABAoIBAAp/xsQXf7gN4CUKbKg3RX+5H/xqQaFPvi5uUCpk3QGBWfdRm+CctSrWSul3ZOD7eD0T7aHrYxJonysw8ex2no6jyN0WmS91ZNYZRBvn6feI/rcwKHwS3NCEjsD+BWZAqx1bGGyivxhQf4fociemCR3ii2MdHygKCzobrKIpX5RvhanI4j01dyLlxwqTsteuc/o5RR4jfg1eN0kldFjk3UcSNyzzEv5o5UhRsHCLJBTNTvYZBN4FpyaqcLT9gKS9aVBvQH63R+E5dyxo1+24tZZricW59h2bN3CFriqkwBo1y0gTnR6VQ22MBvIUxYUm82cxXs/Vr0YQTSAaEGThxFECgYEAxKQMRnM39WMzrNx1WDwpBERRj1T0TbLf1uq6viPiLdik2Tm2aCBZyr5j82Ey7fZ7OafKGfsM0I2AuYeoBdYDuYN6A7tE9kpnECubnWuIvUeYcL+1VzzMedVtdKwQXrYbhqKtyvnSJ9gQ6CusHtsDE1bQvTMxBX4KNBeBYllCUasCgYEAyU0RPUaj56CyLHKty8jNg6wl+06IZ0pUPIWZ//b1zeZrlHGYDp/InxS8huGFapzOg1sbQBS6j3j3YE3Ts6v6FNuIa4pcPQ91YuHiWWQdgVjrCZdleanFWGTjIx12+RGj9vx4voRhNQcHW1YeTvvyj4BN/ECR6GNaoS/ZjBKo1AUCgYEAj6AyxxJJARagG9Y6b2QhoVg1Kjem6UmJbPStyUt0XIAsh+07afqXGxrM7mtEQ8MQZiBD4Y4Y4gs4xkprUzfqKIn7iNYznKDjflAbrXNpwLaWhWPBFCL4RtS4ycsTedoRaNlRjzvBYBDU6H9djHvzVyDF/itx1s0krr+sZSVE51kCgYBxGRinecna+KFCccgNp6s34H+Se2QNzGgZfOKyOjmOTniA9XV+Oe3I2yi1C34fESzCBm0ACuVqeIdcFz3rQ6OFFnbGHP2H3OiR/uFiYepl4uRjBimgOm9DI6Ot9f8DHxMlUGIygEPxPBq5CWCL9egpEeg+4rRXgYLI7w5mMZGjVQKBgQDC4qyH7FK3lLv5JomoK6nNjpyPNBmr0Rt215oM/AWQaxDhFZH5un68ueZ7MfybwXxHHFQ4ZeSwYs006f1XGPNW6qrH6pi/3SCLFuGVfNnLVwCBkm3QaQrxFm3v9LmVCidTNta0l0DrUldZdK8/P31GBxKo/MmYF/f9LO/Mfm/uDg==
- peerId: 12D3KooWT3c7Y5zvWhhjSxd5Ve3GKZi6WCsG6JHxcxgXixRFdBbw - peerId: 12D3KooWFnz9fYCxHAnf2rvPQ7iPZcCprEqyN8kCtVQfN2K1TfqK
address: 127.0.0.1:4432 address: 127.0.0.1:4431
signingKey: 3iiLPj6wMUQpPwTBNZcUgkbXub1jumg4AEV9LfMyFHZVc84GLyAjVbVvH6EAGhcNrxRxL82aW4BimhDZCpLsRCqx5vwj signingKey: IM0BTVQf4LKMUVRTAHxbBXmdz656+G2ssw4WdLc30pRYy6TsVVdh+n03pKXSCdg665tM/9AjQRCbzgvDf9riWg==
encryptionKey: JgG4CcCbae1qEpe7mKXzp7m5hNc56SSyZd9DwUaEStKJrq7RToAC2Vgd3i6hKRwa58zCWeN6Wjc3o6qrdKPEPRvcyEPysamajVo5mdQiUgWAmr97pGEsyjuRjQoC2GY2LvLiEQxEgwFgJxKGMHMiaWMtDfxCDUaDEm4bu5RdMhqRZekAWho6c3WoEeruSr14iX1TrocFNfBkBY7CjEw8kcywXCTNgtvhb2Qiwgj5AxEF4wyw4bzaNA9ctXb1hoHPFVMu6C51pkFY7jUD9zwyH3ukgnAewkGAcPNbKmaTAtMosKRVaAN97mAwXh2VRt1hWmRvVk7r76EjnVKhD4vbsKZc56RVcHTVWRVdhU7FGyPsiE5rSQAz1JQGYzxnZpX7EG77CyrmUGyfueVfRHhwY2oq8A4uQCRaQxSaJHYLowjXSxh8DQ2V6MTqyzti32C27utBYdHzLVCJSGkmdzGwrFcHqsq7nLDxmvJVErPvyReixEe8kFmqopJ3e6LLm8WdYw9K6JYBjXnEfwPzm7Von9sf3dcaGDUHYfttMyeke7fAXJkvPRje69hYVyzdQGAauuojzGkkvQWCSMK1KCMNMznRaPDCNvofrQhYrub24WhmwpKhorufdfW8Cb4T6reBDCtaWVsbuinjtL6F6Sui5aYHJFLJ6e4pPewr1P4EuZYRbMBZwN5KvDLhTGLBuBnaTqUUdF6bj2U22NoRYMogiHiftqKqiexKNDXX1Zg9RQEvxgjuVo6SBW42mVEA8agrLhruRqCmiduJxVrfqLNGeYXHXrcmMEgW7uosJbPXvTcfRvdFWS1ov7oSALvj6vhDQ28Yi9D2ETNdNsfVWAFQuwvPpW7CHQGXTitprVbqH8JYxNZuGygcLmr5efbB22Vzu4ntd1HoraQpG12qeDEUA7tXYUpoYyuSdWwKPjSAMtaQcCSfVrhKQHQuKJargrVrez8vjWuwLfvSucV7ZHe7gjqvYgULdE1ubRCRSd7DuLjEN2Vd6obzV2c3MRet7ZSf4Sp88WM5AuTyW7BjArBc4S3gUQ8rYaiZ8Tu7NCxkEzbFwWRaemZkwfvcsX3XxqjyF37tFSGkEqE5kuBvpZW72675LkDffj7kH1zA8yE6dVujJjWsNYVFJWndUtz5Vy2KCdZAbBgq19q4AtsxWPodU2N3yZXzFAFAzTrxS6V4P7Scpdau1avgRvHLcBQPunA37xaYMy8YMifJwtmRY25mnAQwZAk3eANk7tXwZd58SDnciLNvARJvwKzTQBXcshkwyy52SX8XmXDJsPnRLaHmiYBJ63Yzr5XpZuuAtxb9qrWG2NHCNxfomHokWacV1hjZPPd6ZxT1FuRozB6Qt2NLcyqY7bnTcQJb1jPUaTAGXXCR8WVmmmYo2fDQe8CdBmgyPvbzNTEJUyScBz4RdycB5PZap4SurJCWtHbuMyQbQUB6jJgURDstfXS5Akfe4oruNq9rnYcNtnsDJPtrhXHBqzDizmf1BDxR5FB2RCxzCgeAfg8WQ1Ug9PVAGTzob6ZqCrGXzWXEUniZnf1vjr7QhGKBYXEX9SWDoSMUpP4FreVDTnx15ijRZTV3p8xG5fE9e36TnugRVvTyq7XzmyPBjW2r66f1bior encryptionKey: MIIEpAIBAAKCAQEAm0HILjO7GRYYb0AvESmxdaj6ruIcSHEQIyqhPbfXZSmJNo9wIq89SaYYL4ZTwrF+ykPDJcBA8SjNHGXBPhZY+ejwCDzDyyv42FMs5lKw+/x94Yg++W72sxawtCLVi0RVY1g4UxOlCgAxl3YC9mVYoqQveXN3EsDd0YNK9fWiWP/Xl3KaJ4ErsfW3LZS9rD36dgDsKr9GqeVQf7lGkCkDmivCwHn3uaN/uzHaWvaZ7e7QWE/36vTmMsllTvi0Q9Y+v+HB5isIX9Jve1QmCS//DbDl9IMGdmyg/jlBs63Nk86Qwlw8ft3ttTWNldTpvD4Ycbgj3l59jT4rIvFJ88+5UwIDAQABAoIBAFfUn/1bMIYhlNMi+T15W7YXUTGsIpnstFdgn3T90rGdDM272ZEVl9NZTidck3f516NvMC/kEhkbnuVovyhzlgRS/a97SLxgdNdUPntR3mO/VCtJW27akl9//5j4d9vgXXnlB4AgBeahc2yey1A+xyTDQ0QuyPbn+tSytK5uNlioCeAqH4ruWxcg4t8MnwNQEOsnchrYHfXqJG+XxGn7m60U4oclbObGfxWxYZ85I0B6M5PW71VLkj/eKTvRJcW5ShDKLG5meiUM3KtwUdFRzv9Xi4aB9eTwEQ8ZV18KVmIF7baBy5anWDfGO4O9MvFSMmbMCe3EkrGaEaCp/gXenhkCgYEAw57dj7ewVHIAQxcNZ9SPRUNAY8g9yEYQ//30yTcpUjsGlqGNzua2OvALGL2ntFY304X9Iego+7Tzxs3T0x2FQ1N33NhoxwRcMqBdksMqmCb8Bm8UvnFIuvmsfPGkkzwa/8xNH81GZiz0p9zfi4lSKdZRfTQ4lBqvogExdnalSd0CgYEAyy2Mw4eeJQ0Y6QX3nad2/06oxWiS3++CITI6dAqiepAB6V7lnP90NKfLgzJcCJwzKlMhoVv7Lu4bDCXbvQ03ba+Dl+To8Jf5/9di8j8OfllqDWPnbqyueTHu5CUk+A2Gz3RhjmMXHpVgbFkUJTkJ1RDWPImNq0KzTYQ+ZwU8lO8CgYEAo1/0zuisnXowedew3HyLw17tUeiUoMTTwdiJLduh6Qle8UKvupK4svRzcBBFFbnEGiaXSFAqmj2AMxMHzBOljpsRSiJ7L2uWzLleLQpOcpBsf7sZ6guWoIGQ6zCtMEJMkkJAT0UTfJYjJmazVEg1lLdni1enwRmggX7ZnoRsewkCgYB2SpLF1FOSpsl2Ae9kbnettRI1vOimUD+nLCM0JGzshqNWR9XPTjtN3NN0EwHaUXbIkZXm6DKZ5C8DJ5eDvgojZihrau7kBNecyL3m5CeAEHbaTOwVV5xNG3FGiwm3EckHR271A2QWfkmhS0ubUFYVIrRYko1UxIS4AOKEAFyBKQKBgQCfIsGy4wOqRsKLzbun6fIVSz8X+sS3HclD7UZ6WKanOYyWZ420qnhLnfdDcNQjF/ApbzHdfwwTKVJSaZiK23kmReUVB9Cq2YAHMJYlp0ErgPzZstrRiRidtzJHm93owWc7GZinzd1M8EOYUSJ3+t8EZXZlbsD/oCTbX/BGqolo2w==
space:
gcTTL: 60
syncPeriod: 10

View File

@ -1,22 +0,0 @@
anytype:
swarmKey: /key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec
grpcServer:
listenAddrs:
- 127.0.0.1:4430
- 127.0.0.1:4431
tls: false
account:
peerId: 12D3KooWMHuhZgK2skkLrvL51QQTXaXQKYy2QqfvPNBFnzR2ubA1
signingKey: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug
encryptionKey: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd
apiServer:
port: "8080"
nodes:
- peerId: 12D3KooWMHuhZgK2skkLrvL51QQTXaXQKYy2QqfvPNBFnzR2ubA1
address: 127.0.0.1:4430
signingKey: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug
encryptionKey: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd
- peerId: 12D3KooWT3c7Y5zvWhhjSxd5Ve3GKZi6WCsG6JHxcxgXixRFdBbw
address: 127.0.0.1:4432
signingKey: 3iiLPj6wMUQpPwTBNZcUgkbXub1jumg4AEV9LfMyFHZVc84GLyAjVbVvH6EAGhcNrxRxL82aW4BimhDZCpLsRCqx5vwj
encryptionKey: JgG4CcCbae1qEpe7mKXzp7m5hNc56SSyZd9DwUaEStKJrq7RToAC2Vgd3i6hKRwa58zCWeN6Wjc3o6qrdKPEPRvcyEPysamajVo5mdQiUgWAmr97pGEsyjuRjQoC2GY2LvLiEQxEgwFgJxKGMHMiaWMtDfxCDUaDEm4bu5RdMhqRZekAWho6c3WoEeruSr14iX1TrocFNfBkBY7CjEw8kcywXCTNgtvhb2Qiwgj5AxEF4wyw4bzaNA9ctXb1hoHPFVMu6C51pkFY7jUD9zwyH3ukgnAewkGAcPNbKmaTAtMosKRVaAN97mAwXh2VRt1hWmRvVk7r76EjnVKhD4vbsKZc56RVcHTVWRVdhU7FGyPsiE5rSQAz1JQGYzxnZpX7EG77CyrmUGyfueVfRHhwY2oq8A4uQCRaQxSaJHYLowjXSxh8DQ2V6MTqyzti32C27utBYdHzLVCJSGkmdzGwrFcHqsq7nLDxmvJVErPvyReixEe8kFmqopJ3e6LLm8WdYw9K6JYBjXnEfwPzm7Von9sf3dcaGDUHYfttMyeke7fAXJkvPRje69hYVyzdQGAauuojzGkkvQWCSMK1KCMNMznRaPDCNvofrQhYrub24WhmwpKhorufdfW8Cb4T6reBDCtaWVsbuinjtL6F6Sui5aYHJFLJ6e4pPewr1P4EuZYRbMBZwN5KvDLhTGLBuBnaTqUUdF6bj2U22NoRYMogiHiftqKqiexKNDXX1Zg9RQEvxgjuVo6SBW42mVEA8agrLhruRqCmiduJxVrfqLNGeYXHXrcmMEgW7uosJbPXvTcfRvdFWS1ov7oSALvj6vhDQ28Yi9D2ETNdNsfVWAFQuwvPpW7CHQGXTitprVbqH8JYxNZuGygcLmr5efbB22Vzu4ntd1HoraQpG12qeDEUA7tXYUpoYyuSdWwKPjSAMtaQcCSfVrhKQHQuKJargrVrez8vjWuwLfvSucV7ZHe7gjqvYgULdE1ubRCRSd7DuLjEN2Vd6obzV2c3MRet7ZSf4Sp88WM5AuTyW7BjArBc4S3gUQ8rYaiZ8Tu7NCxkEzbFwWRaemZkwfvcsX3XxqjyF37tFSGkEqE5kuBvpZW72675LkDffj7kH1zA8yE6dVujJjWsNYVFJWndUtz5Vy2KCdZAbBgq19q4AtsxWPodU2N3yZXzFAFAzTrxS6V4P7Scpdau1avgRvHLcBQPunA37xaYMy8YMifJwtmRY25mnAQwZAk3eANk7tXwZd58SDnciLNvARJvwKzTQBXcshkwyy52SX8XmXDJsPnRLaHmiYBJ63Yzr5XpZuuAtxb9qrWG2NHCNxfomHokWacV1hjZPPd6ZxT1FuRozB6Qt2NLcyqY7bnTcQJb1jPUaTAGXXCR8WVmmmYo2fDQe8CdBmgyPvbzNTEJUyScBz4RdycB5PZap4SurJCWtHbuMyQbQUB6jJgURDstfXS5Akfe4oruNq9rnYcNtnsDJPtrhXHBqzDizmf1BDxR5FB2RCxzCgeAfg8WQ1Ug9PVAGTzob6ZqCrGXzWXEUniZnf1vjr7QhGKBYXEX9SWDoSMUpP4FreVDTnx15ijRZTV3p8xG5fE9e36TnugRVvTyq7XzmyPBjW2r66f1bior

View File

@ -1,22 +0,0 @@
anytype:
swarmKey: /key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec
grpcServer:
listenAddrs:
- 127.0.0.1:4432
- 127.0.0.1:4433
tls: false
account:
peerId: 12D3KooWT3c7Y5zvWhhjSxd5Ve3GKZi6WCsG6JHxcxgXixRFdBbw
signingKey: 3iiLPj6wMUQpPwTBNZcUgkbXub1jumg4AEV9LfMyFHZVc84GLyAjVbVvH6EAGhcNrxRxL82aW4BimhDZCpLsRCqx5vwj
encryptionKey: JgG4CcCbae1qEpe7mKXzp7m5hNc56SSyZd9DwUaEStKJrq7RToAC2Vgd3i6hKRwa58zCWeN6Wjc3o6qrdKPEPRvcyEPysamajVo5mdQiUgWAmr97pGEsyjuRjQoC2GY2LvLiEQxEgwFgJxKGMHMiaWMtDfxCDUaDEm4bu5RdMhqRZekAWho6c3WoEeruSr14iX1TrocFNfBkBY7CjEw8kcywXCTNgtvhb2Qiwgj5AxEF4wyw4bzaNA9ctXb1hoHPFVMu6C51pkFY7jUD9zwyH3ukgnAewkGAcPNbKmaTAtMosKRVaAN97mAwXh2VRt1hWmRvVk7r76EjnVKhD4vbsKZc56RVcHTVWRVdhU7FGyPsiE5rSQAz1JQGYzxnZpX7EG77CyrmUGyfueVfRHhwY2oq8A4uQCRaQxSaJHYLowjXSxh8DQ2V6MTqyzti32C27utBYdHzLVCJSGkmdzGwrFcHqsq7nLDxmvJVErPvyReixEe8kFmqopJ3e6LLm8WdYw9K6JYBjXnEfwPzm7Von9sf3dcaGDUHYfttMyeke7fAXJkvPRje69hYVyzdQGAauuojzGkkvQWCSMK1KCMNMznRaPDCNvofrQhYrub24WhmwpKhorufdfW8Cb4T6reBDCtaWVsbuinjtL6F6Sui5aYHJFLJ6e4pPewr1P4EuZYRbMBZwN5KvDLhTGLBuBnaTqUUdF6bj2U22NoRYMogiHiftqKqiexKNDXX1Zg9RQEvxgjuVo6SBW42mVEA8agrLhruRqCmiduJxVrfqLNGeYXHXrcmMEgW7uosJbPXvTcfRvdFWS1ov7oSALvj6vhDQ28Yi9D2ETNdNsfVWAFQuwvPpW7CHQGXTitprVbqH8JYxNZuGygcLmr5efbB22Vzu4ntd1HoraQpG12qeDEUA7tXYUpoYyuSdWwKPjSAMtaQcCSfVrhKQHQuKJargrVrez8vjWuwLfvSucV7ZHe7gjqvYgULdE1ubRCRSd7DuLjEN2Vd6obzV2c3MRet7ZSf4Sp88WM5AuTyW7BjArBc4S3gUQ8rYaiZ8Tu7NCxkEzbFwWRaemZkwfvcsX3XxqjyF37tFSGkEqE5kuBvpZW72675LkDffj7kH1zA8yE6dVujJjWsNYVFJWndUtz5Vy2KCdZAbBgq19q4AtsxWPodU2N3yZXzFAFAzTrxS6V4P7Scpdau1avgRvHLcBQPunA37xaYMy8YMifJwtmRY25mnAQwZAk3eANk7tXwZd58SDnciLNvARJvwKzTQBXcshkwyy52SX8XmXDJsPnRLaHmiYBJ63Yzr5XpZuuAtxb9qrWG2NHCNxfomHokWacV1hjZPPd6ZxT1FuRozB6Qt2NLcyqY7bnTcQJb1jPUaTAGXXCR8WVmmmYo2fDQe8CdBmgyPvbzNTEJUyScBz4RdycB5PZap4SurJCWtHbuMyQbQUB6jJgURDstfXS5Akfe4oruNq9rnYcNtnsDJPtrhXHBqzDizmf1BDxR5FB2RCxzCgeAfg8WQ1Ug9PVAGTzob6ZqCrGXzWXEUniZnf1vjr7QhGKBYXEX9SWDoSMUpP4FreVDTnx15ijRZTV3p8xG5fE9e36TnugRVvTyq7XzmyPBjW2r66f1bior
apiServer:
port: "8081"
nodes:
- peerId: 12D3KooWMHuhZgK2skkLrvL51QQTXaXQKYy2QqfvPNBFnzR2ubA1
address: 127.0.0.1:4430
signingKey: 3id6ddLcoNoe9rDgGM88ET8T6TnvHm5GFqFdN6kBzn7Q8d6VUGgjeT59CNWFiaofdeRnHBvX2A5ZacMXvfwaYEFuCbug
encryptionKey: JgG4CcCbae1qEpe7mKpBzsHjZhXUmDSNVNX2B1gxFZsJyMX4V6kBQUott9zRWyeXaW1ZmpzuxDXnwSQpAnNurhXyGa9iQaAPqzY9A9VWBPD33Yy1eW7TRuVemzToh8jJQKQKnZNbF8ucTWV9qahusKzyvN8uyhrqoW2tAPfA9S3E3ognCuqbLSW6yjE2rBKayvyS1BVwzjSd6FZK4DDyjfU3pbEVjut3wytGEAn9af6sNMmyCnf2MX5vLovWs9rU8av61wD4z7HTsXyGFx4K75N4Go249Hpe9SKAT6HxhRc3yvj63krPLiQV5yMuH2UeMUXBDekUQyNmBEdn9wrur7mLqB67Bc6tcc2PP8XApBCdWJHvHjN4FktSpaG5vbCqoZbLD1oCbk36q2x9s6XM8pydVqD1J9P3nTbfgMb5pJCTFjNtgKeuKv6wjfJeA9jF1VhcJQisfsahgv9MvZ9M8FJpZTq1zKUhYDCRnZxUkraoMS5yNNVdDzaUckKEDthqik7BMWCWT79vq7uVgMwEvGwGi76gtoMg1159bbPMLZ4bdPVfhH2S9QjPrzQfwZSrzB2YeVPjWpaXDeLDity5H8n1NK2oniAQR6gE71n81neSptsuhV6o6QpQ89AU8y57XmEsou4VEryn8vUxBHhULLxrLNUouxyWamCeFiDjk5cSN6koQsf9BYKSNTPFTrwjTKForDokMhcPdMtFktKwjv7u9UEGcY4MKvNzZZkc77gHiP8bqVtdNNoLpTFUC5SZ9i7bKdHvK12HpSy7yzzPeMXJ9UwhLxkok1g81ngTbN1yxRhvYXyHZFtguCR9kvGojDjka91MTBtk551qDw9eCn2xZT9U8jqzBCjdpvSg3mRWKMPnYAGB7m7u1ye165wyGFvzcHAx3vtXjxAqLUeKYZCjv2m6V9D2Y4qH1TQNddWqH14T1JVMis971UCH9Ddpj6a3387oUnufD1P6HZN2ieJCvptrmbGVvxJYYSvmVf1dkwbtqurDRNWD7TJ7gf6iqSP549C9bxP4GpLt3ygjHmMtcuUzstBuztvunJUnQhfnJxqU6LjRdsFzm53wGWgXNxab7ZvQcPyLwsevn1b98FGPnVpS5iY4LjmqW4ugrC6HgrbsjrXiKzR1yZKhLQkCbLzPoaHb8iB5iBnCr7d4yf5CtfpFRqgoqMFdK5LNZYmDX4HzUKN6A7wC3gGiSRFTLcgGZeSMkB5Pa61CZBU7WCQgFxykycE9HRA7PiQa496GWDCV15teToCpFRsAa6jDmR1MGXPeLRqQgve49VXnQN5FL7c1VuEv5SWjeTuCnMB47DJKBaP7eKJNKgLwETALzSCMF3nRiRgeb15kfoS4BbrJ5yupjrvwmbmvNg1AYFFS5sYNWft7K8v87wQvBakRtGP71Kp8NX77XFtu6xdB7sR6jpfC6qJPyB9akWNXgCrWy9kE4ih42gwAZdUugNZ9YtEsgRM3pwb6qJhkAPyEJtrxrja859PCAgqPSQiPQN33PaMkgQ6HJknu8CrjKRiXAycZ16KLUkHV64TNhEjPTcX1a7rqpD131AYMWX8d7CCdc9Ys7RUb6BwguuNSh8rJK3x4AkMDSUsaE8ynKvpC7RXZpJ9Nxfhd
- peerId: 12D3KooWT3c7Y5zvWhhjSxd5Ve3GKZi6WCsG6JHxcxgXixRFdBbw
address: 127.0.0.1:4432
signingKey: 3iiLPj6wMUQpPwTBNZcUgkbXub1jumg4AEV9LfMyFHZVc84GLyAjVbVvH6EAGhcNrxRxL82aW4BimhDZCpLsRCqx5vwj
encryptionKey: JgG4CcCbae1qEpe7mKXzp7m5hNc56SSyZd9DwUaEStKJrq7RToAC2Vgd3i6hKRwa58zCWeN6Wjc3o6qrdKPEPRvcyEPysamajVo5mdQiUgWAmr97pGEsyjuRjQoC2GY2LvLiEQxEgwFgJxKGMHMiaWMtDfxCDUaDEm4bu5RdMhqRZekAWho6c3WoEeruSr14iX1TrocFNfBkBY7CjEw8kcywXCTNgtvhb2Qiwgj5AxEF4wyw4bzaNA9ctXb1hoHPFVMu6C51pkFY7jUD9zwyH3ukgnAewkGAcPNbKmaTAtMosKRVaAN97mAwXh2VRt1hWmRvVk7r76EjnVKhD4vbsKZc56RVcHTVWRVdhU7FGyPsiE5rSQAz1JQGYzxnZpX7EG77CyrmUGyfueVfRHhwY2oq8A4uQCRaQxSaJHYLowjXSxh8DQ2V6MTqyzti32C27utBYdHzLVCJSGkmdzGwrFcHqsq7nLDxmvJVErPvyReixEe8kFmqopJ3e6LLm8WdYw9K6JYBjXnEfwPzm7Von9sf3dcaGDUHYfttMyeke7fAXJkvPRje69hYVyzdQGAauuojzGkkvQWCSMK1KCMNMznRaPDCNvofrQhYrub24WhmwpKhorufdfW8Cb4T6reBDCtaWVsbuinjtL6F6Sui5aYHJFLJ6e4pPewr1P4EuZYRbMBZwN5KvDLhTGLBuBnaTqUUdF6bj2U22NoRYMogiHiftqKqiexKNDXX1Zg9RQEvxgjuVo6SBW42mVEA8agrLhruRqCmiduJxVrfqLNGeYXHXrcmMEgW7uosJbPXvTcfRvdFWS1ov7oSALvj6vhDQ28Yi9D2ETNdNsfVWAFQuwvPpW7CHQGXTitprVbqH8JYxNZuGygcLmr5efbB22Vzu4ntd1HoraQpG12qeDEUA7tXYUpoYyuSdWwKPjSAMtaQcCSfVrhKQHQuKJargrVrez8vjWuwLfvSucV7ZHe7gjqvYgULdE1ubRCRSd7DuLjEN2Vd6obzV2c3MRet7ZSf4Sp88WM5AuTyW7BjArBc4S3gUQ8rYaiZ8Tu7NCxkEzbFwWRaemZkwfvcsX3XxqjyF37tFSGkEqE5kuBvpZW72675LkDffj7kH1zA8yE6dVujJjWsNYVFJWndUtz5Vy2KCdZAbBgq19q4AtsxWPodU2N3yZXzFAFAzTrxS6V4P7Scpdau1avgRvHLcBQPunA37xaYMy8YMifJwtmRY25mnAQwZAk3eANk7tXwZd58SDnciLNvARJvwKzTQBXcshkwyy52SX8XmXDJsPnRLaHmiYBJ63Yzr5XpZuuAtxb9qrWG2NHCNxfomHokWacV1hjZPPd6ZxT1FuRozB6Qt2NLcyqY7bnTcQJb1jPUaTAGXXCR8WVmmmYo2fDQe8CdBmgyPvbzNTEJUyScBz4RdycB5PZap4SurJCWtHbuMyQbQUB6jJgURDstfXS5Akfe4oruNq9rnYcNtnsDJPtrhXHBqzDizmf1BDxR5FB2RCxzCgeAfg8WQ1Ug9PVAGTzob6ZqCrGXzWXEUniZnf1vjr7QhGKBYXEX9SWDoSMUpP4FreVDTnx15ijRZTV3p8xG5fE9e36TnugRVvTyq7XzmyPBjW2r66f1bior

24
etc/configs/config1.yml Executable file
View File

@ -0,0 +1,24 @@
anytype:
swarmKey: /key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec
grpcServer:
listenAddrs:
- 127.0.0.1:4430
tls: false
account:
peerId: 12D3KooWSUx2LXPvoZGp72Dt7b7r1kPSmQ6zAUwKkHFyX64uiXRu
signingKey: 4QTrtkFLQe9wQcWT/cgFEwfMHB5pt4axInNmCIMCZaz3nVdyygRoO8/YH0V15X6Mnw1NQWsS1YIWiLS22hwepA==
encryptionKey: MIIEpAIBAAKCAQEAmqAAOPfR86po3m+zwSzbAlZGgMMF188v35Ulqf9Gb4KO8DZ9ifxrqpjlLZRxPKeXj3wSSQEXsJf3A82rZlDxddZSM0i7Mx5G2G0zRHWx9dC58PpX6o/fDuvSwcyXqOgIK55N/hyEuIbWQgp5Rk9uy2Zbrhv5ZL5CvceM0b9wSKt/hRvntxSbG+HRgXWaQvAReGuJrySVvkh6fhC3G0IwqyFbGNq2zqAJej6NBzZA3thHgTn5PoWD8O4cyukBxunKGu3HLE3vJtqEMFrkNFw5SMpdEtxyTLN6T1HIeYCY9RL+BFYfxIWg6pGtIoIJKUB0XapJr9ltzvXfT9KeSCU0VwIDAQABAoIBAAp/xsQXf7gN4CUKbKg3RX+5H/xqQaFPvi5uUCpk3QGBWfdRm+CctSrWSul3ZOD7eD0T7aHrYxJonysw8ex2no6jyN0WmS91ZNYZRBvn6feI/rcwKHwS3NCEjsD+BWZAqx1bGGyivxhQf4fociemCR3ii2MdHygKCzobrKIpX5RvhanI4j01dyLlxwqTsteuc/o5RR4jfg1eN0kldFjk3UcSNyzzEv5o5UhRsHCLJBTNTvYZBN4FpyaqcLT9gKS9aVBvQH63R+E5dyxo1+24tZZricW59h2bN3CFriqkwBo1y0gTnR6VQ22MBvIUxYUm82cxXs/Vr0YQTSAaEGThxFECgYEAxKQMRnM39WMzrNx1WDwpBERRj1T0TbLf1uq6viPiLdik2Tm2aCBZyr5j82Ey7fZ7OafKGfsM0I2AuYeoBdYDuYN6A7tE9kpnECubnWuIvUeYcL+1VzzMedVtdKwQXrYbhqKtyvnSJ9gQ6CusHtsDE1bQvTMxBX4KNBeBYllCUasCgYEAyU0RPUaj56CyLHKty8jNg6wl+06IZ0pUPIWZ//b1zeZrlHGYDp/InxS8huGFapzOg1sbQBS6j3j3YE3Ts6v6FNuIa4pcPQ91YuHiWWQdgVjrCZdleanFWGTjIx12+RGj9vx4voRhNQcHW1YeTvvyj4BN/ECR6GNaoS/ZjBKo1AUCgYEAj6AyxxJJARagG9Y6b2QhoVg1Kjem6UmJbPStyUt0XIAsh+07afqXGxrM7mtEQ8MQZiBD4Y4Y4gs4xkprUzfqKIn7iNYznKDjflAbrXNpwLaWhWPBFCL4RtS4ycsTedoRaNlRjzvBYBDU6H9djHvzVyDF/itx1s0krr+sZSVE51kCgYBxGRinecna+KFCccgNp6s34H+Se2QNzGgZfOKyOjmOTniA9XV+Oe3I2yi1C34fESzCBm0ACuVqeIdcFz3rQ6OFFnbGHP2H3OiR/uFiYepl4uRjBimgOm9DI6Ot9f8DHxMlUGIygEPxPBq5CWCL9egpEeg+4rRXgYLI7w5mMZGjVQKBgQDC4qyH7FK3lLv5JomoK6nNjpyPNBmr0Rt215oM/AWQaxDhFZH5un68ueZ7MfybwXxHHFQ4ZeSwYs006f1XGPNW6qrH6pi/3SCLFuGVfNnLVwCBkm3QaQrxFm3v9LmVCidTNta0l0DrUldZdK8/P31GBxKo/MmYF/f9LO/Mfm/uDg==
apiServer:
port: "8080"
nodes:
- peerId: 12D3KooWSUx2LXPvoZGp72Dt7b7r1kPSmQ6zAUwKkHFyX64uiXRu
address: 127.0.0.1:4430
signingKey: 4QTrtkFLQe9wQcWT/cgFEwfMHB5pt4axInNmCIMCZaz3nVdyygRoO8/YH0V15X6Mnw1NQWsS1YIWiLS22hwepA==
encryptionKey: MIIEpAIBAAKCAQEAmqAAOPfR86po3m+zwSzbAlZGgMMF188v35Ulqf9Gb4KO8DZ9ifxrqpjlLZRxPKeXj3wSSQEXsJf3A82rZlDxddZSM0i7Mx5G2G0zRHWx9dC58PpX6o/fDuvSwcyXqOgIK55N/hyEuIbWQgp5Rk9uy2Zbrhv5ZL5CvceM0b9wSKt/hRvntxSbG+HRgXWaQvAReGuJrySVvkh6fhC3G0IwqyFbGNq2zqAJej6NBzZA3thHgTn5PoWD8O4cyukBxunKGu3HLE3vJtqEMFrkNFw5SMpdEtxyTLN6T1HIeYCY9RL+BFYfxIWg6pGtIoIJKUB0XapJr9ltzvXfT9KeSCU0VwIDAQABAoIBAAp/xsQXf7gN4CUKbKg3RX+5H/xqQaFPvi5uUCpk3QGBWfdRm+CctSrWSul3ZOD7eD0T7aHrYxJonysw8ex2no6jyN0WmS91ZNYZRBvn6feI/rcwKHwS3NCEjsD+BWZAqx1bGGyivxhQf4fociemCR3ii2MdHygKCzobrKIpX5RvhanI4j01dyLlxwqTsteuc/o5RR4jfg1eN0kldFjk3UcSNyzzEv5o5UhRsHCLJBTNTvYZBN4FpyaqcLT9gKS9aVBvQH63R+E5dyxo1+24tZZricW59h2bN3CFriqkwBo1y0gTnR6VQ22MBvIUxYUm82cxXs/Vr0YQTSAaEGThxFECgYEAxKQMRnM39WMzrNx1WDwpBERRj1T0TbLf1uq6viPiLdik2Tm2aCBZyr5j82Ey7fZ7OafKGfsM0I2AuYeoBdYDuYN6A7tE9kpnECubnWuIvUeYcL+1VzzMedVtdKwQXrYbhqKtyvnSJ9gQ6CusHtsDE1bQvTMxBX4KNBeBYllCUasCgYEAyU0RPUaj56CyLHKty8jNg6wl+06IZ0pUPIWZ//b1zeZrlHGYDp/InxS8huGFapzOg1sbQBS6j3j3YE3Ts6v6FNuIa4pcPQ91YuHiWWQdgVjrCZdleanFWGTjIx12+RGj9vx4voRhNQcHW1YeTvvyj4BN/ECR6GNaoS/ZjBKo1AUCgYEAj6AyxxJJARagG9Y6b2QhoVg1Kjem6UmJbPStyUt0XIAsh+07afqXGxrM7mtEQ8MQZiBD4Y4Y4gs4xkprUzfqKIn7iNYznKDjflAbrXNpwLaWhWPBFCL4RtS4ycsTedoRaNlRjzvBYBDU6H9djHvzVyDF/itx1s0krr+sZSVE51kCgYBxGRinecna+KFCccgNp6s34H+Se2QNzGgZfOKyOjmOTniA9XV+Oe3I2yi1C34fESzCBm0ACuVqeIdcFz3rQ6OFFnbGHP2H3OiR/uFiYepl4uRjBimgOm9DI6Ot9f8DHxMlUGIygEPxPBq5CWCL9egpEeg+4rRXgYLI7w5mMZGjVQKBgQDC4qyH7FK3lLv5JomoK6nNjpyPNBmr0Rt215oM/AWQaxDhFZH5un68ueZ7MfybwXxHHFQ4ZeSwYs006f1XGPNW6qrH6pi/3SCLFuGVfNnLVwCBkm3QaQrxFm3v9LmVCidTNta0l0DrUldZdK8/P31GBxKo/MmYF/f9LO/Mfm/uDg==
- peerId: 12D3KooWFnz9fYCxHAnf2rvPQ7iPZcCprEqyN8kCtVQfN2K1TfqK
address: 127.0.0.1:4431
signingKey: IM0BTVQf4LKMUVRTAHxbBXmdz656+G2ssw4WdLc30pRYy6TsVVdh+n03pKXSCdg665tM/9AjQRCbzgvDf9riWg==
encryptionKey: MIIEpAIBAAKCAQEAm0HILjO7GRYYb0AvESmxdaj6ruIcSHEQIyqhPbfXZSmJNo9wIq89SaYYL4ZTwrF+ykPDJcBA8SjNHGXBPhZY+ejwCDzDyyv42FMs5lKw+/x94Yg++W72sxawtCLVi0RVY1g4UxOlCgAxl3YC9mVYoqQveXN3EsDd0YNK9fWiWP/Xl3KaJ4ErsfW3LZS9rD36dgDsKr9GqeVQf7lGkCkDmivCwHn3uaN/uzHaWvaZ7e7QWE/36vTmMsllTvi0Q9Y+v+HB5isIX9Jve1QmCS//DbDl9IMGdmyg/jlBs63Nk86Qwlw8ft3ttTWNldTpvD4Ycbgj3l59jT4rIvFJ88+5UwIDAQABAoIBAFfUn/1bMIYhlNMi+T15W7YXUTGsIpnstFdgn3T90rGdDM272ZEVl9NZTidck3f516NvMC/kEhkbnuVovyhzlgRS/a97SLxgdNdUPntR3mO/VCtJW27akl9//5j4d9vgXXnlB4AgBeahc2yey1A+xyTDQ0QuyPbn+tSytK5uNlioCeAqH4ruWxcg4t8MnwNQEOsnchrYHfXqJG+XxGn7m60U4oclbObGfxWxYZ85I0B6M5PW71VLkj/eKTvRJcW5ShDKLG5meiUM3KtwUdFRzv9Xi4aB9eTwEQ8ZV18KVmIF7baBy5anWDfGO4O9MvFSMmbMCe3EkrGaEaCp/gXenhkCgYEAw57dj7ewVHIAQxcNZ9SPRUNAY8g9yEYQ//30yTcpUjsGlqGNzua2OvALGL2ntFY304X9Iego+7Tzxs3T0x2FQ1N33NhoxwRcMqBdksMqmCb8Bm8UvnFIuvmsfPGkkzwa/8xNH81GZiz0p9zfi4lSKdZRfTQ4lBqvogExdnalSd0CgYEAyy2Mw4eeJQ0Y6QX3nad2/06oxWiS3++CITI6dAqiepAB6V7lnP90NKfLgzJcCJwzKlMhoVv7Lu4bDCXbvQ03ba+Dl+To8Jf5/9di8j8OfllqDWPnbqyueTHu5CUk+A2Gz3RhjmMXHpVgbFkUJTkJ1RDWPImNq0KzTYQ+ZwU8lO8CgYEAo1/0zuisnXowedew3HyLw17tUeiUoMTTwdiJLduh6Qle8UKvupK4svRzcBBFFbnEGiaXSFAqmj2AMxMHzBOljpsRSiJ7L2uWzLleLQpOcpBsf7sZ6guWoIGQ6zCtMEJMkkJAT0UTfJYjJmazVEg1lLdni1enwRmggX7ZnoRsewkCgYB2SpLF1FOSpsl2Ae9kbnettRI1vOimUD+nLCM0JGzshqNWR9XPTjtN3NN0EwHaUXbIkZXm6DKZ5C8DJ5eDvgojZihrau7kBNecyL3m5CeAEHbaTOwVV5xNG3FGiwm3EckHR271A2QWfkmhS0ubUFYVIrRYko1UxIS4AOKEAFyBKQKBgQCfIsGy4wOqRsKLzbun6fIVSz8X+sS3HclD7UZ6WKanOYyWZ420qnhLnfdDcNQjF/ApbzHdfwwTKVJSaZiK23kmReUVB9Cq2YAHMJYlp0ErgPzZstrRiRidtzJHm93owWc7GZinzd1M8EOYUSJ3+t8EZXZlbsD/oCTbX/BGqolo2w==
space:
gcTTL: 60
syncPeriod: 10

24
etc/configs/config2.yml Executable file
View File

@ -0,0 +1,24 @@
anytype:
swarmKey: /key/swarm/psk/1.0.0/base16/209992e611c27d5dce8fbd2e7389f6b51da9bee980992ef60739460b536139ec
grpcServer:
listenAddrs:
- 127.0.0.1:4431
tls: false
account:
peerId: 12D3KooWFnz9fYCxHAnf2rvPQ7iPZcCprEqyN8kCtVQfN2K1TfqK
signingKey: IM0BTVQf4LKMUVRTAHxbBXmdz656+G2ssw4WdLc30pRYy6TsVVdh+n03pKXSCdg665tM/9AjQRCbzgvDf9riWg==
encryptionKey: MIIEpAIBAAKCAQEAm0HILjO7GRYYb0AvESmxdaj6ruIcSHEQIyqhPbfXZSmJNo9wIq89SaYYL4ZTwrF+ykPDJcBA8SjNHGXBPhZY+ejwCDzDyyv42FMs5lKw+/x94Yg++W72sxawtCLVi0RVY1g4UxOlCgAxl3YC9mVYoqQveXN3EsDd0YNK9fWiWP/Xl3KaJ4ErsfW3LZS9rD36dgDsKr9GqeVQf7lGkCkDmivCwHn3uaN/uzHaWvaZ7e7QWE/36vTmMsllTvi0Q9Y+v+HB5isIX9Jve1QmCS//DbDl9IMGdmyg/jlBs63Nk86Qwlw8ft3ttTWNldTpvD4Ycbgj3l59jT4rIvFJ88+5UwIDAQABAoIBAFfUn/1bMIYhlNMi+T15W7YXUTGsIpnstFdgn3T90rGdDM272ZEVl9NZTidck3f516NvMC/kEhkbnuVovyhzlgRS/a97SLxgdNdUPntR3mO/VCtJW27akl9//5j4d9vgXXnlB4AgBeahc2yey1A+xyTDQ0QuyPbn+tSytK5uNlioCeAqH4ruWxcg4t8MnwNQEOsnchrYHfXqJG+XxGn7m60U4oclbObGfxWxYZ85I0B6M5PW71VLkj/eKTvRJcW5ShDKLG5meiUM3KtwUdFRzv9Xi4aB9eTwEQ8ZV18KVmIF7baBy5anWDfGO4O9MvFSMmbMCe3EkrGaEaCp/gXenhkCgYEAw57dj7ewVHIAQxcNZ9SPRUNAY8g9yEYQ//30yTcpUjsGlqGNzua2OvALGL2ntFY304X9Iego+7Tzxs3T0x2FQ1N33NhoxwRcMqBdksMqmCb8Bm8UvnFIuvmsfPGkkzwa/8xNH81GZiz0p9zfi4lSKdZRfTQ4lBqvogExdnalSd0CgYEAyy2Mw4eeJQ0Y6QX3nad2/06oxWiS3++CITI6dAqiepAB6V7lnP90NKfLgzJcCJwzKlMhoVv7Lu4bDCXbvQ03ba+Dl+To8Jf5/9di8j8OfllqDWPnbqyueTHu5CUk+A2Gz3RhjmMXHpVgbFkUJTkJ1RDWPImNq0KzTYQ+ZwU8lO8CgYEAo1/0zuisnXowedew3HyLw17tUeiUoMTTwdiJLduh6Qle8UKvupK4svRzcBBFFbnEGiaXSFAqmj2AMxMHzBOljpsRSiJ7L2uWzLleLQpOcpBsf7sZ6guWoIGQ6zCtMEJMkkJAT0UTfJYjJmazVEg1lLdni1enwRmggX7ZnoRsewkCgYB2SpLF1FOSpsl2Ae9kbnettRI1vOimUD+nLCM0JGzshqNWR9XPTjtN3NN0EwHaUXbIkZXm6DKZ5C8DJ5eDvgojZihrau7kBNecyL3m5CeAEHbaTOwVV5xNG3FGiwm3EckHR271A2QWfkmhS0ubUFYVIrRYko1UxIS4AOKEAFyBKQKBgQCfIsGy4wOqRsKLzbun6fIVSz8X+sS3HclD7UZ6WKanOYyWZ420qnhLnfdDcNQjF/ApbzHdfwwTKVJSaZiK23kmReUVB9Cq2YAHMJYlp0ErgPzZstrRiRidtzJHm93owWc7GZinzd1M8EOYUSJ3+t8EZXZlbsD/oCTbX/BGqolo2w==
apiServer:
port: "8081"
nodes:
- peerId: 12D3KooWSUx2LXPvoZGp72Dt7b7r1kPSmQ6zAUwKkHFyX64uiXRu
address: 127.0.0.1:4430
signingKey: 4QTrtkFLQe9wQcWT/cgFEwfMHB5pt4axInNmCIMCZaz3nVdyygRoO8/YH0V15X6Mnw1NQWsS1YIWiLS22hwepA==
encryptionKey: MIIEpAIBAAKCAQEAmqAAOPfR86po3m+zwSzbAlZGgMMF188v35Ulqf9Gb4KO8DZ9ifxrqpjlLZRxPKeXj3wSSQEXsJf3A82rZlDxddZSM0i7Mx5G2G0zRHWx9dC58PpX6o/fDuvSwcyXqOgIK55N/hyEuIbWQgp5Rk9uy2Zbrhv5ZL5CvceM0b9wSKt/hRvntxSbG+HRgXWaQvAReGuJrySVvkh6fhC3G0IwqyFbGNq2zqAJej6NBzZA3thHgTn5PoWD8O4cyukBxunKGu3HLE3vJtqEMFrkNFw5SMpdEtxyTLN6T1HIeYCY9RL+BFYfxIWg6pGtIoIJKUB0XapJr9ltzvXfT9KeSCU0VwIDAQABAoIBAAp/xsQXf7gN4CUKbKg3RX+5H/xqQaFPvi5uUCpk3QGBWfdRm+CctSrWSul3ZOD7eD0T7aHrYxJonysw8ex2no6jyN0WmS91ZNYZRBvn6feI/rcwKHwS3NCEjsD+BWZAqx1bGGyivxhQf4fociemCR3ii2MdHygKCzobrKIpX5RvhanI4j01dyLlxwqTsteuc/o5RR4jfg1eN0kldFjk3UcSNyzzEv5o5UhRsHCLJBTNTvYZBN4FpyaqcLT9gKS9aVBvQH63R+E5dyxo1+24tZZricW59h2bN3CFriqkwBo1y0gTnR6VQ22MBvIUxYUm82cxXs/Vr0YQTSAaEGThxFECgYEAxKQMRnM39WMzrNx1WDwpBERRj1T0TbLf1uq6viPiLdik2Tm2aCBZyr5j82Ey7fZ7OafKGfsM0I2AuYeoBdYDuYN6A7tE9kpnECubnWuIvUeYcL+1VzzMedVtdKwQXrYbhqKtyvnSJ9gQ6CusHtsDE1bQvTMxBX4KNBeBYllCUasCgYEAyU0RPUaj56CyLHKty8jNg6wl+06IZ0pUPIWZ//b1zeZrlHGYDp/InxS8huGFapzOg1sbQBS6j3j3YE3Ts6v6FNuIa4pcPQ91YuHiWWQdgVjrCZdleanFWGTjIx12+RGj9vx4voRhNQcHW1YeTvvyj4BN/ECR6GNaoS/ZjBKo1AUCgYEAj6AyxxJJARagG9Y6b2QhoVg1Kjem6UmJbPStyUt0XIAsh+07afqXGxrM7mtEQ8MQZiBD4Y4Y4gs4xkprUzfqKIn7iNYznKDjflAbrXNpwLaWhWPBFCL4RtS4ycsTedoRaNlRjzvBYBDU6H9djHvzVyDF/itx1s0krr+sZSVE51kCgYBxGRinecna+KFCccgNp6s34H+Se2QNzGgZfOKyOjmOTniA9XV+Oe3I2yi1C34fESzCBm0ACuVqeIdcFz3rQ6OFFnbGHP2H3OiR/uFiYepl4uRjBimgOm9DI6Ot9f8DHxMlUGIygEPxPBq5CWCL9egpEeg+4rRXgYLI7w5mMZGjVQKBgQDC4qyH7FK3lLv5JomoK6nNjpyPNBmr0Rt215oM/AWQaxDhFZH5un68ueZ7MfybwXxHHFQ4ZeSwYs006f1XGPNW6qrH6pi/3SCLFuGVfNnLVwCBkm3QaQrxFm3v9LmVCidTNta0l0DrUldZdK8/P31GBxKo/MmYF/f9LO/Mfm/uDg==
- peerId: 12D3KooWFnz9fYCxHAnf2rvPQ7iPZcCprEqyN8kCtVQfN2K1TfqK
address: 127.0.0.1:4431
signingKey: IM0BTVQf4LKMUVRTAHxbBXmdz656+G2ssw4WdLc30pRYy6TsVVdh+n03pKXSCdg665tM/9AjQRCbzgvDf9riWg==
encryptionKey: MIIEpAIBAAKCAQEAm0HILjO7GRYYb0AvESmxdaj6ruIcSHEQIyqhPbfXZSmJNo9wIq89SaYYL4ZTwrF+ykPDJcBA8SjNHGXBPhZY+ejwCDzDyyv42FMs5lKw+/x94Yg++W72sxawtCLVi0RVY1g4UxOlCgAxl3YC9mVYoqQveXN3EsDd0YNK9fWiWP/Xl3KaJ4ErsfW3LZS9rD36dgDsKr9GqeVQf7lGkCkDmivCwHn3uaN/uzHaWvaZ7e7QWE/36vTmMsllTvi0Q9Y+v+HB5isIX9Jve1QmCS//DbDl9IMGdmyg/jlBs63Nk86Qwlw8ft3ttTWNldTpvD4Ycbgj3l59jT4rIvFJ88+5UwIDAQABAoIBAFfUn/1bMIYhlNMi+T15W7YXUTGsIpnstFdgn3T90rGdDM272ZEVl9NZTidck3f516NvMC/kEhkbnuVovyhzlgRS/a97SLxgdNdUPntR3mO/VCtJW27akl9//5j4d9vgXXnlB4AgBeahc2yey1A+xyTDQ0QuyPbn+tSytK5uNlioCeAqH4ruWxcg4t8MnwNQEOsnchrYHfXqJG+XxGn7m60U4oclbObGfxWxYZ85I0B6M5PW71VLkj/eKTvRJcW5ShDKLG5meiUM3KtwUdFRzv9Xi4aB9eTwEQ8ZV18KVmIF7baBy5anWDfGO4O9MvFSMmbMCe3EkrGaEaCp/gXenhkCgYEAw57dj7ewVHIAQxcNZ9SPRUNAY8g9yEYQ//30yTcpUjsGlqGNzua2OvALGL2ntFY304X9Iego+7Tzxs3T0x2FQ1N33NhoxwRcMqBdksMqmCb8Bm8UvnFIuvmsfPGkkzwa/8xNH81GZiz0p9zfi4lSKdZRfTQ4lBqvogExdnalSd0CgYEAyy2Mw4eeJQ0Y6QX3nad2/06oxWiS3++CITI6dAqiepAB6V7lnP90NKfLgzJcCJwzKlMhoVv7Lu4bDCXbvQ03ba+Dl+To8Jf5/9di8j8OfllqDWPnbqyueTHu5CUk+A2Gz3RhjmMXHpVgbFkUJTkJ1RDWPImNq0KzTYQ+ZwU8lO8CgYEAo1/0zuisnXowedew3HyLw17tUeiUoMTTwdiJLduh6Qle8UKvupK4svRzcBBFFbnEGiaXSFAqmj2AMxMHzBOljpsRSiJ7L2uWzLleLQpOcpBsf7sZ6guWoIGQ6zCtMEJMkkJAT0UTfJYjJmazVEg1lLdni1enwRmggX7ZnoRsewkCgYB2SpLF1FOSpsl2Ae9kbnettRI1vOimUD+nLCM0JGzshqNWR9XPTjtN3NN0EwHaUXbIkZXm6DKZ5C8DJ5eDvgojZihrau7kBNecyL3m5CeAEHbaTOwVV5xNG3FGiwm3EckHR271A2QWfkmhS0ubUFYVIrRYko1UxIS4AOKEAFyBKQKBgQCfIsGy4wOqRsKLzbun6fIVSz8X+sS3HclD7UZ6WKanOYyWZ420qnhLnfdDcNQjF/ApbzHdfwwTKVJSaZiK23kmReUVB9Cq2YAHMJYlp0ErgPzZstrRiRidtzJHm93owWc7GZinzd1M8EOYUSJ3+t8EZXZlbsD/oCTbX/BGqolo2w==
space:
gcTTL: 60
syncPeriod: 10

4
go.mod
View File

@ -3,6 +3,7 @@ module github.com/anytypeio/go-anytype-infrastructure-experiments
go 1.18 go 1.18
require ( require (
github.com/anytypeio/go-chash v0.0.0-20220629194632-4ad1154fe232
github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab
github.com/cespare/xxhash v1.1.0 github.com/cespare/xxhash v1.1.0
github.com/goccy/go-graphviz v0.0.9 github.com/goccy/go-graphviz v0.0.9
@ -16,6 +17,7 @@ require (
github.com/multiformats/go-multihash v0.1.0 github.com/multiformats/go-multihash v0.1.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
github.com/zeebo/blake3 v0.2.3 github.com/zeebo/blake3 v0.2.3
github.com/zeebo/errs v1.3.0
go.uber.org/zap v1.21.0 go.uber.org/zap v1.21.0
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
@ -24,7 +26,6 @@ require (
require ( require (
github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/anytypeio/go-chash v0.0.0-20220629194632-4ad1154fe232 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect github.com/btcsuite/btcd v0.22.1 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
@ -46,7 +47,6 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/zeebo/errs v1.2.2 // indirect
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect

4
go.sum
View File

@ -113,8 +113,8 @@ github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g= github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=

63
node/account/service.go Normal file
View File

@ -0,0 +1,63 @@
package account
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
commonaccount "github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
)
type service struct {
accountData *account.AccountData
peerId string
}
func (s *service) Account() *account.AccountData {
return s.accountData
}
func New() app.Component {
return &service{}
}
func (s *service) Init(a *app.App) (err error) {
cfg := a.MustComponent(config.CName).(*config.Config)
acc := cfg.Account
decodedEncryptionKey, err := keys.DecodeKeyFromString(
acc.EncryptionKey,
encryptionkey.NewEncryptionRsaPrivKeyFromBytes,
nil)
if err != nil {
return err
}
decodedSigningKey, err := keys.DecodeKeyFromString(
acc.SigningKey,
signingkey.NewSigningEd25519PrivKeyFromBytes,
nil)
if err != nil {
return err
}
identity, err := decodedSigningKey.GetPublic().Raw()
if err != nil {
return err
}
s.accountData = &account.AccountData{
Identity: identity,
SignKey: decodedSigningKey,
EncKey: decodedEncryptionKey,
}
s.peerId = acc.PeerId
return nil
}
func (s *service) Name() (name string) {
return commonaccount.CName
}

View File

@ -0,0 +1,85 @@
package nodecache
import (
"context"
"errors"
"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/node/nodespace"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache"
"time"
)
var log = logger.NewNamed("treecache")
var ErrCacheObjectWithoutTree = errors.New("cache object contains no tree")
type ctxKey int
const spaceKey ctxKey = 0
type treeCache struct {
gcttl int
cache ocache.OCache
nodeService nodespace.Service
}
func (c *treeCache) Run(ctx context.Context) (err error) {
return nil
}
func (c *treeCache) Close(ctx context.Context) (err error) {
return c.cache.Close()
}
func (c *treeCache) Init(a *app.App) (err error) {
c.nodeService = a.MustComponent(nodespace.CName).(nodespace.Service)
c.cache = ocache.New(
func(ctx context.Context, id string) (value ocache.Object, err error) {
spaceId := ctx.Value(spaceKey).(string)
space, err := c.nodeService.GetSpace(ctx, spaceId)
if err != nil {
return
}
return space.BuildTree(ctx, id, nil)
},
ocache.WithLogger(log.Sugar()),
ocache.WithGCPeriod(time.Minute),
ocache.WithTTL(time.Duration(c.gcttl)*time.Second),
ocache.WithRefCounter(false),
)
return nil
}
func (c *treeCache) Name() (name string) {
return cache.CName
}
func NewNodeCache(ttl int) cache.TreeCache {
return &treeCache{
gcttl: ttl,
}
}
func (c *treeCache) GetTree(ctx context.Context, spaceId, id string) (res cache.TreeResult, err error) {
var cacheRes ocache.Object
ctx = context.WithValue(ctx, spaceKey, spaceId)
cacheRes, err = c.cache.Get(ctx, id)
if err != nil {
return cache.TreeResult{}, err
}
treeContainer, ok := cacheRes.(cache.TreeContainer)
if !ok {
err = ErrCacheObjectWithoutTree
return
}
res = cache.TreeResult{
Release: func() {
c.cache.Release(id)
},
TreeContainer: treeContainer,
}
return
}

View File

@ -0,0 +1,59 @@
package nodespace
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/cache"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
)
type rpcHandler struct {
s *service
}
func (r *rpcHandler) PushSpace(ctx context.Context, req *spacesyncproto.PushSpaceRequest) (resp *spacesyncproto.PushSpaceResponse, err error) {
_, err = r.s.GetSpace(ctx, req.SpaceId)
if err == nil {
err = spacesyncproto.ErrSpaceExists
return
}
if err != cache.ErrSpaceNotFound {
err = spacesyncproto.ErrUnexpected
return
}
payload := storage.SpaceStorageCreatePayload{
RecWithId: req.AclRoot,
SpaceHeader: req.SpaceHeader,
Id: req.SpaceId,
}
_, err = r.s.spaceStorageProvider.CreateSpaceStorage(payload)
if err != nil {
err = spacesyncproto.ErrUnexpected
if err == storage.ErrSpaceStorageExists {
err = spacesyncproto.ErrSpaceExists
}
return
}
return
}
func (r *rpcHandler) HeadSync(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (*spacesyncproto.HeadSyncResponse, error) {
sp, err := r.s.GetSpace(ctx, req.SpaceId)
if err != nil {
return nil, spacesyncproto.ErrSpaceMissing
}
return sp.SpaceSyncRpc().HeadSync(ctx, req)
}
func (r *rpcHandler) Stream(stream spacesyncproto.DRPCSpace_StreamStream) error {
msg, err := stream.Recv()
if err != nil {
return err
}
sp, err := r.s.GetSpace(stream.Context(), msg.SpaceId)
if err != nil {
return spacesyncproto.ErrSpaceMissing
}
return sp.SpaceSyncRpc().Stream(stream)
}

74
node/nodespace/service.go Normal file
View File

@ -0,0 +1,74 @@
package nodespace
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"
"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/net/rpc/server"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/ocache"
"time"
)
const CName = "node.nodespace"
var log = logger.NewNamed(CName)
func New() Service {
return &service{}
}
type Service interface {
GetSpace(ctx context.Context, id string) (commonspace.Space, error)
app.ComponentRunnable
}
type service struct {
conf config.Space
spaceCache ocache.OCache
commonSpace commonspace.Service
spaceStorageProvider storage.SpaceStorageProvider
}
func (s *service) Init(a *app.App) (err error) {
s.conf = a.MustComponent(config.CName).(*config.Config).Space
s.commonSpace = a.MustComponent(commonspace.CName).(commonspace.Service)
s.spaceStorageProvider = a.MustComponent(storage.CName).(storage.SpaceStorageProvider)
s.spaceCache = ocache.New(
func(ctx context.Context, id string) (value ocache.Object, err error) {
return s.commonSpace.GetSpace(ctx, id)
},
ocache.WithLogger(log.Sugar()),
ocache.WithGCPeriod(time.Minute),
ocache.WithTTL(time.Duration(s.conf.GCTTL)*time.Second),
ocache.WithRefCounter(false),
)
return spacesyncproto.DRPCRegisterSpace(a.MustComponent(server.CName).(server.DRPCServer), &rpcHandler{s})
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) Run(ctx context.Context) (err error) {
go func() {
time.Sleep(time.Second * 5)
_, _ = s.GetSpace(ctx, "testDSpace")
}()
return
}
func (s *service) GetSpace(ctx context.Context, id string) (commonspace.Space, error) {
v, err := s.spaceCache.Get(ctx, id)
if err != nil {
return nil, err
}
return v.(commonspace.Space), nil
}
func (s *service) Close(ctx context.Context) (err error) {
return s.spaceCache.Close()
}

View File

@ -1,14 +1,12 @@
package account package account
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
) )
type AccountData struct { // TODO: create a convenient constructor for this type AccountData struct { // TODO: create a convenient constructor for this
Identity string // TODO: this is essentially the same as sign key Identity []byte // public key
SignKey signingkey.PrivKey SignKey signingkey.PrivKey
EncKey encryptionkey.PrivKey EncKey encryptionkey.PrivKey
Decoder keys.Decoder
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,148 +0,0 @@
syntax = "proto3";
package acl;
option go_package = "aclpb";
message RawChange {
bytes payload = 1;
bytes signature = 2;
string id = 3;
}
message RawRecord {
bytes payload = 1;
bytes signature = 2;
string id = 3;
}
// the element of change tree used to store and internal apply smartBlock history
message ACLChange {
repeated string treeHeadIds = 1;
repeated string aclHeadIds = 2;
string snapshotBaseId = 3; // we will only have one base snapshot for both
ACLData aclData = 4;
// the data is encoded with read key and should be read in ChangesData format
bytes changesData = 5;
uint64 currentReadKeyHash = 6;
int64 timestamp = 7;
string identity = 8;
message ACLContentValue {
oneof value {
UserAdd userAdd = 1;
UserRemove userRemove = 2;
UserPermissionChange userPermissionChange = 3;
UserInvite userInvite = 4;
UserJoin userJoin = 5;
UserConfirm userConfirm = 6;
}
}
message ACLData {
ACLSnapshot aclSnapshot = 1;
repeated ACLContentValue aclContent = 2;
}
message ACLSnapshot {
// We don't need ACLState as a separate message now, because we simplified the snapshot model
ACLState aclState = 1;
}
message ACLState {
repeated uint64 readKeyHashes = 1;
repeated UserState userStates = 2;
map<string, UserInvite> invites = 3; // TODO: later
// repeated string unconfirmedUsers = 4; // TODO: later
}
message UserState {
string identity = 1;
bytes encryptionKey = 2;
repeated bytes encryptedReadKeys = 3; // all read keys that we know
UserPermissions permissions = 4;
bool IsConfirmed = 5;
}
// we already know identity and encryptionKey
message UserAdd {
string identity = 1; // public signing key
bytes encryptionKey = 2; // public encryption key
repeated bytes encryptedReadKeys = 3; // all read keys that we know for the user
UserPermissions permissions = 4;
}
// TODO: this is not used as of now
message UserConfirm { // not needed for read permissions
string identity = 1; // not needed
string userAddId = 2;
}
message UserInvite {
bytes acceptPublicKey = 1;
bytes encryptPublicKey = 2;
repeated bytes encryptedReadKeys = 3; // all read keys that we know for the user
UserPermissions permissions = 4;
string InviteId = 5;
}
message UserJoin {
string identity = 1;
bytes encryptionKey = 2;
bytes acceptSignature = 3; // sign acceptPublicKey
string userInviteId = 4;
repeated bytes encryptedReadKeys = 5; // the idea is that user should itself reencrypt the keys with the pub key
}
message UserRemove {
string identity = 1;
repeated ReadKeyReplace readKeyReplaces = 3; // new read key encrypted for all users
}
message ReadKeyReplace {
string identity = 1;
bytes encryptionKey = 2;
bytes encryptedReadKey = 3;
}
message UserPermissionChange {
string identity = 1;
UserPermissions permissions = 2;
}
enum UserPermissions {
Admin = 0;
Writer = 1;
Reader = 2;
Removed = 3;
}
}
message Change {
repeated string treeHeadIds = 1;
string aclHeadId = 2;
string snapshotBaseId = 3; // we will only have one base snapshot for both
bytes changesData = 4;
uint64 currentReadKeyHash = 5;
int64 timestamp = 6;
string identity = 7;
bool isSnapshot = 8;
}
message Record {
string prevId = 1;
string identity = 2;
bytes data = 3;
uint64 currentReadKeyHash = 4;
int64 timestamp = 5;
}
message Header {
string firstId = 1;
string aclListId = 2;
string workspaceId = 3;
DocType docType = 4;
enum DocType {
ACL = 0;
DocTree = 1;
}
}

View File

@ -1,12 +0,0 @@
package aclchanges
import (
"github.com/gogo/protobuf/proto"
)
type Change interface {
ProtoChange() proto.Marshaler
DecryptedChangeContent() []byte
Signature() []byte
CID() string
}

View File

@ -0,0 +1,10 @@
package aclrecordproto
import "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
func ACLReadKeyDerive(signKey []byte, encKey []byte) (*symmetric.Key, error) {
concBuf := make([]byte, 0, len(signKey)+len(encKey))
concBuf = append(concBuf, signKey...)
concBuf = append(concBuf, encKey...)
return symmetric.DeriveFromBytes(concBuf)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
syntax = "proto3";
package aclrecord;
option go_package = "pkg/acl/aclrecordproto";
message RawACLRecord {
bytes payload = 1;
bytes signature = 2;
}
message RawACLRecordWithId {
bytes payload = 1;
string id = 2;
}
message ACLRecord {
string prevId = 1;
bytes identity = 2;
bytes data = 3;
uint64 currentReadKeyHash = 4;
int64 timestamp = 5;
}
message ACLRoot {
bytes identity = 1;
bytes encryptionKey = 2;
string spaceId = 3;
bytes encryptedReadKey = 4;
string derivationScheme = 5;
uint64 currentReadKeyHash = 6;
int64 timestamp = 7;
}
message ACLContentValue {
oneof value {
ACLUserAdd userAdd = 1;
ACLUserRemove userRemove = 2;
ACLUserPermissionChange userPermissionChange = 3;
ACLUserInvite userInvite = 4;
ACLUserJoin userJoin = 5;
}
}
message ACLData {
repeated ACLContentValue aclContent = 1;
}
message ACLState {
repeated uint64 readKeyHashes = 1;
repeated ACLUserState userStates = 2;
map<string, ACLUserInvite> invites = 3;
}
message ACLUserState {
bytes identity = 1;
bytes encryptionKey = 2;
ACLUserPermissions permissions = 3;
}
message ACLUserAdd {
bytes identity = 1;
bytes encryptionKey = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
}
message ACLUserInvite {
bytes acceptPublicKey = 1;
bytes encryptPublicKey = 2;
repeated bytes encryptedReadKeys = 3;
ACLUserPermissions permissions = 4;
string inviteId = 5;
}
message ACLUserJoin {
bytes identity = 1;
bytes encryptionKey = 2;
bytes acceptSignature = 3;
string inviteId = 4;
repeated bytes encryptedReadKeys = 5;
}
message ACLUserRemove {
bytes identity = 1;
repeated ACLReadKeyReplace readKeyReplaces = 3;
}
message ACLReadKeyReplace {
bytes identity = 1;
bytes encryptionKey = 2;
bytes encryptedReadKey = 3;
}
message ACLUserPermissionChange {
bytes identity = 1;
ACLUserPermissions permissions = 2;
}
enum ACLUserPermissions {
Admin = 0;
Writer = 1;
Reader = 2;
}

View File

@ -1,19 +1,16 @@
package common package common
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
) )
type Keychain struct { type Keychain struct {
decoder keys.Decoder keys map[string]signingkey.PubKey
keys map[string]signingkey.PubKey
} }
func NewKeychain() *Keychain { func NewKeychain() *Keychain {
return &Keychain{ return &Keychain{
decoder: signingkey.NewEDPubKeyDecoder(), keys: make(map[string]signingkey.PubKey),
keys: make(map[string]signingkey.PubKey),
} }
} }
@ -21,7 +18,7 @@ func (k *Keychain) GetOrAdd(identity string) (signingkey.PubKey, error) {
if key, exists := k.keys[identity]; exists { if key, exists := k.keys[identity]; exists {
return key, nil return key, nil
} }
res, err := k.decoder.DecodeFromString(identity) res, err := signingkey.NewSigningEd25519PubKeyFromBytes([]byte(identity))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -0,0 +1,95 @@
package list
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/gogo/protobuf/proto"
)
type ACLRecordBuilder interface {
ConvertFromRaw(rawIdRecord *aclrecordproto.RawACLRecordWithId) (rec *ACLRecord, err error)
}
type aclRecordBuilder struct {
id string
keychain *common.Keychain
}
func newACLRecordBuilder(id string, keychain *common.Keychain) ACLRecordBuilder {
return &aclRecordBuilder{
id: id,
keychain: keychain,
}
}
func (a *aclRecordBuilder) ConvertFromRaw(rawIdRecord *aclrecordproto.RawACLRecordWithId) (rec *ACLRecord, err error) {
rawRec := &aclrecordproto.RawACLRecord{}
err = proto.Unmarshal(rawIdRecord.Payload, rawRec)
if err != nil {
return
}
if rawIdRecord.Id == a.id {
aclRoot := &aclrecordproto.ACLRoot{}
err = proto.Unmarshal(rawRec.Payload, aclRoot)
if err != nil {
return
}
rec = &ACLRecord{
Id: rawIdRecord.Id,
CurrentReadKeyHash: aclRoot.CurrentReadKeyHash,
Timestamp: aclRoot.Timestamp,
Signature: rawRec.Signature,
Identity: aclRoot.Identity,
Model: aclRoot,
}
} else {
aclRecord := &aclrecordproto.ACLRecord{}
err = proto.Unmarshal(rawRec.Payload, aclRecord)
if err != nil {
return
}
rec = &ACLRecord{
Id: rawIdRecord.Id,
PrevId: aclRecord.PrevId,
CurrentReadKeyHash: aclRecord.CurrentReadKeyHash,
Timestamp: aclRecord.Timestamp,
Data: aclRecord.Data,
Signature: rawRec.Signature,
Identity: aclRecord.Identity,
}
}
err = verifyRaw(a.keychain, rawRec, rawIdRecord, rec.Identity)
return
}
func verifyRaw(
keychain *common.Keychain,
rawRec *aclrecordproto.RawACLRecord,
recWithId *aclrecordproto.RawACLRecordWithId,
identity []byte) (err error) {
identityKey, err := keychain.GetOrAdd(string(identity))
if err != nil {
return
}
// verifying signature
res, err := identityKey.Verify(rawRec.Payload, rawRec.Signature)
if err != nil {
return
}
if !res {
err = ErrInvalidSignature
return
}
// verifying ID
if !cid.VerifyCID(recWithId.Payload, recWithId.Id) {
err = ErrIncorrectCID
}
return
}

View File

@ -1,12 +1,12 @@
package list package list
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger" "github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
@ -26,20 +26,21 @@ var ErrNoSuchRecord = errors.New("no such record")
var ErrInsufficientPermissions = errors.New("insufficient permissions") var ErrInsufficientPermissions = errors.New("insufficient permissions")
var ErrNoReadKey = errors.New("acl state doesn't have a read key") var ErrNoReadKey = errors.New("acl state doesn't have a read key")
var ErrInvalidSignature = errors.New("signature is invalid") var ErrInvalidSignature = errors.New("signature is invalid")
var ErrIncorrectRoot = errors.New("incorrect root")
type UserPermissionPair struct { type UserPermissionPair struct {
Identity string Identity string
Permission aclpb.ACLChangeUserPermissions Permission aclrecordproto.ACLUserPermissions
} }
type ACLState struct { type ACLState struct {
id string
currentReadKeyHash uint64 currentReadKeyHash uint64
userReadKeys map[uint64]*symmetric.Key userReadKeys map[uint64]*symmetric.Key
userStates map[string]*aclpb.ACLChangeUserState userStates map[string]*aclrecordproto.ACLUserState
userInvites map[string]*aclpb.ACLChangeUserInvite userInvites map[string]*aclrecordproto.ACLUserInvite
encryptionKey encryptionkey.PrivKey
signingPubKeyDecoder keys.Decoder signingKey signingkey.PrivKey
encryptionKey encryptionkey.PrivKey
identity string identity string
permissionsAtRecord map[string][]UserPermissionPair permissionsAtRecord map[string][]UserPermissionPair
@ -47,30 +48,33 @@ type ACLState struct {
keychain *common.Keychain keychain *common.Keychain
} }
func newACLStateWithIdentity( func newACLStateWithKeys(
identity string, id string,
encryptionKey encryptionkey.PrivKey, signingKey signingkey.PrivKey,
decoder keys.Decoder) *ACLState { encryptionKey encryptionkey.PrivKey) (*ACLState, error) {
return &ACLState{ identity, err := signingKey.Raw()
identity: identity, if err != nil {
encryptionKey: encryptionKey, return nil, err
userReadKeys: make(map[uint64]*symmetric.Key),
userStates: make(map[string]*aclpb.ACLChangeUserState),
userInvites: make(map[string]*aclpb.ACLChangeUserInvite),
signingPubKeyDecoder: decoder,
permissionsAtRecord: make(map[string][]UserPermissionPair),
keychain: common.NewKeychain(),
} }
return &ACLState{
id: id,
identity: string(identity),
signingKey: signingKey,
encryptionKey: encryptionKey,
userReadKeys: make(map[uint64]*symmetric.Key),
userStates: make(map[string]*aclrecordproto.ACLUserState),
userInvites: make(map[string]*aclrecordproto.ACLUserInvite),
permissionsAtRecord: make(map[string][]UserPermissionPair),
}, nil
} }
func newACLState(decoder keys.Decoder) *ACLState { func newACLState(id string) *ACLState {
return &ACLState{ return &ACLState{
signingPubKeyDecoder: decoder, id: id,
userReadKeys: make(map[uint64]*symmetric.Key), userReadKeys: make(map[uint64]*symmetric.Key),
userStates: make(map[string]*aclpb.ACLChangeUserState), userStates: make(map[string]*aclrecordproto.ACLUserState),
userInvites: make(map[string]*aclpb.ACLChangeUserInvite), userInvites: make(map[string]*aclrecordproto.ACLUserInvite),
permissionsAtRecord: make(map[string][]UserPermissionPair), permissionsAtRecord: make(map[string][]UserPermissionPair),
keychain: common.NewKeychain(),
} }
} }
@ -105,40 +109,27 @@ func (st *ACLState) PermissionsAtRecord(id string, identity string) (UserPermiss
return UserPermissionPair{}, ErrNoSuchUser return UserPermissionPair{}, ErrNoSuchUser
} }
func (st *ACLState) applyRecord(record *aclpb.Record) (err error) { func (st *ACLState) applyRecord(record *ACLRecord) (err error) {
aclData := &aclpb.ACLChangeACLData{} if record.Id == st.id {
root, ok := record.Model.(*aclrecordproto.ACLRoot)
err = proto.Unmarshal(record.Data, aclData) if !ok {
if err != nil { return ErrIncorrectRoot
return }
return st.applyRoot(root)
} }
aclData := &aclrecordproto.ACLData{}
err = st.applyChangeData(aclData, record.CurrentReadKeyHash, record.Identity) if record.Model != nil {
if err != nil { aclData = record.Model.(*aclrecordproto.ACLData)
return
}
st.currentReadKeyHash = record.CurrentReadKeyHash
return
}
func (st *ACLState) applyChangeAndUpdate(recordWrapper *Record) (err error) {
var (
change = recordWrapper.Content
aclData = &aclpb.ACLChangeACLData{}
)
if recordWrapper.Model != nil {
aclData = recordWrapper.Model.(*aclpb.ACLChangeACLData)
} else { } else {
err = proto.Unmarshal(change.Data, aclData) err = proto.Unmarshal(record.Data, aclData)
if err != nil { if err != nil {
return return
} }
recordWrapper.Model = aclData record.Model = aclData
} }
err = st.applyChangeData(aclData, recordWrapper.Content.CurrentReadKeyHash, recordWrapper.Content.Identity) err = st.applyChangeData(aclData, record.CurrentReadKeyHash, record.Identity)
if err != nil { if err != nil {
return return
} }
@ -147,17 +138,68 @@ func (st *ACLState) applyChangeAndUpdate(recordWrapper *Record) (err error) {
var permissions []UserPermissionPair var permissions []UserPermissionPair
for _, state := range st.userStates { for _, state := range st.userStates {
permission := UserPermissionPair{ permission := UserPermissionPair{
Identity: state.Identity, Identity: string(state.Identity),
Permission: state.Permissions, Permission: state.Permissions,
} }
permissions = append(permissions, permission) permissions = append(permissions, permission)
} }
st.permissionsAtRecord[recordWrapper.Id] = permissions st.permissionsAtRecord[record.Id] = permissions
return nil return nil
} }
func (st *ACLState) applyChangeData(changeData *aclpb.ACLChangeACLData, hash uint64, identity string) (err error) { func (st *ACLState) applyRoot(root *aclrecordproto.ACLRoot) (err error) {
if st.signingKey != nil && st.encryptionKey != nil {
err = st.saveReadKeyFromRoot(root)
if err != nil {
return
}
}
// adding user to the list
userState := &aclrecordproto.ACLUserState{
Identity: root.Identity,
EncryptionKey: root.EncryptionKey,
Permissions: aclrecordproto.ACLUserPermissions_Admin,
}
st.userStates[string(root.Identity)] = userState
return
}
func (st *ACLState) saveReadKeyFromRoot(root *aclrecordproto.ACLRoot) (err error) {
var readKey *symmetric.Key
if len(root.GetDerivationScheme()) != 0 {
var encPubKey []byte
encPubKey, err = st.encryptionKey.GetPublic().Raw()
if err != nil {
return
}
readKey, err = aclrecordproto.ACLReadKeyDerive([]byte(st.identity), encPubKey)
if err != nil {
return
}
} else {
readKey, _, err = st.decryptReadKeyAndHash(root.EncryptedReadKey)
if err != nil {
return
}
}
hasher := fnv.New64()
_, err = hasher.Write(readKey.Bytes())
if err != nil {
return
}
if hasher.Sum64() != root.CurrentReadKeyHash {
return ErrIncorrectRoot
}
st.currentReadKeyHash = root.CurrentReadKeyHash
st.userReadKeys[root.CurrentReadKeyHash] = readKey
return
}
func (st *ACLState) applyChangeData(changeData *aclrecordproto.ACLData, hash uint64, identity []byte) (err error) {
defer func() { defer func() {
if err != nil { if err != nil {
return return
@ -165,17 +207,14 @@ func (st *ACLState) applyChangeData(changeData *aclpb.ACLChangeACLData, hash uin
st.currentReadKeyHash = hash st.currentReadKeyHash = hash
}() }()
// we can't check this for the user which is joining, because it will not be in our list if !st.isUserJoin(changeData) {
// the same is for the first change to be added // we check signature when we add this to the List, so no need to do it here
skipIdentityCheck := st.isUserJoin(changeData) || (st.currentReadKeyHash == 0 && st.isUserAdd(changeData, identity)) if _, exists := st.userStates[string(identity)]; !exists {
if !skipIdentityCheck {
// we check signature when we add this to the Tree, so no need to do it here
if _, exists := st.userStates[identity]; !exists {
err = ErrNoSuchUser err = ErrNoSuchUser
return return
} }
if !st.hasPermission(identity, aclpb.ACLChange_Admin) { if !st.hasPermission(identity, aclrecordproto.ACLUserPermissions_Admin) {
err = fmt.Errorf("user %s must have admin permissions", identity) err = fmt.Errorf("user %s must have admin permissions", identity)
return return
} }
@ -191,7 +230,7 @@ func (st *ACLState) applyChangeData(changeData *aclpb.ACLChangeACLData, hash uin
return nil return nil
} }
func (st *ACLState) applyChangeContent(ch *aclpb.ACLChangeACLContentValue) error { func (st *ACLState) applyChangeContent(ch *aclrecordproto.ACLContentValue) error {
switch { switch {
case ch.GetUserPermissionChange() != nil: case ch.GetUserPermissionChange() != nil:
return st.applyUserPermissionChange(ch.GetUserPermissionChange()) return st.applyUserPermissionChange(ch.GetUserPermissionChange())
@ -203,50 +242,46 @@ func (st *ACLState) applyChangeContent(ch *aclpb.ACLChangeACLContentValue) error
return st.applyUserInvite(ch.GetUserInvite()) return st.applyUserInvite(ch.GetUserInvite())
case ch.GetUserJoin() != nil: case ch.GetUserJoin() != nil:
return st.applyUserJoin(ch.GetUserJoin()) return st.applyUserJoin(ch.GetUserJoin())
case ch.GetUserConfirm() != nil:
return st.applyUserConfirm(ch.GetUserConfirm())
default: default:
return fmt.Errorf("unexpected change type: %v", ch) return fmt.Errorf("unexpected change type: %v", ch)
} }
} }
func (st *ACLState) applyUserPermissionChange(ch *aclpb.ACLChangeUserPermissionChange) error { func (st *ACLState) applyUserPermissionChange(ch *aclrecordproto.ACLUserPermissionChange) error {
if _, exists := st.userStates[ch.Identity]; !exists { chIdentity := string(ch.Identity)
state, exists := st.userStates[chIdentity]
if !exists {
return ErrNoSuchUser return ErrNoSuchUser
} }
st.userStates[ch.Identity].Permissions = ch.Permissions state.Permissions = ch.Permissions
return nil return nil
} }
func (st *ACLState) applyUserInvite(ch *aclpb.ACLChangeUserInvite) error { func (st *ACLState) applyUserInvite(ch *aclrecordproto.ACLUserInvite) error {
st.userInvites[ch.InviteId] = ch st.userInvites[ch.InviteId] = ch
return nil return nil
} }
func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error { func (st *ACLState) applyUserJoin(ch *aclrecordproto.ACLUserJoin) error {
invite, exists := st.userInvites[ch.UserInviteId] invite, exists := st.userInvites[ch.InviteId]
if !exists { if !exists {
return fmt.Errorf("no such invite with id %s", ch.UserInviteId) return fmt.Errorf("no such invite with id %s", ch.InviteId)
} }
chIdentity := string(ch.Identity)
if _, exists = st.userStates[ch.Identity]; exists { if _, exists = st.userStates[chIdentity]; exists {
return ErrUserAlreadyExists return ErrUserAlreadyExists
} }
// validating signature // validating signature
signature := ch.GetAcceptSignature() signature := ch.GetAcceptSignature()
verificationKey, err := st.signingPubKeyDecoder.DecodeFromBytes(invite.AcceptPublicKey) verificationKey, err := signingkey.NewSigningEd25519PubKeyFromBytes(invite.AcceptPublicKey)
if err != nil { if err != nil {
return fmt.Errorf("public key verifying invite accepts is given in incorrect format: %v", err) return fmt.Errorf("public key verifying invite accepts is given in incorrect format: %v", err)
} }
rawSignedId, err := st.signingPubKeyDecoder.DecodeFromStringIntoBytes(ch.Identity) res, err := verificationKey.(signingkey.PubKey).Verify(ch.Identity, signature)
if err != nil {
return fmt.Errorf("failed to decode signing identity as bytes")
}
res, err := verificationKey.(signingkey.PubKey).Verify(rawSignedId, signature)
if err != nil { if err != nil {
return fmt.Errorf("verification returned error: %w", err) return fmt.Errorf("verification returned error: %w", err)
} }
@ -255,7 +290,7 @@ func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error {
} }
// if ourselves -> we need to decrypt the read keys // if ourselves -> we need to decrypt the read keys
if st.identity == ch.Identity { if st.identity == chIdentity {
for _, key := range ch.EncryptedReadKeys { for _, key := range ch.EncryptedReadKeys {
key, hash, err := st.decryptReadKeyAndHash(key) key, hash, err := st.decryptReadKeyAndHash(key)
if err != nil { if err != nil {
@ -267,30 +302,28 @@ func (st *ACLState) applyUserJoin(ch *aclpb.ACLChangeUserJoin) error {
} }
// adding user to the list // adding user to the list
userState := &aclpb.ACLChangeUserState{ userState := &aclrecordproto.ACLUserState{
Identity: ch.Identity, Identity: ch.Identity,
EncryptionKey: ch.EncryptionKey, EncryptionKey: ch.EncryptionKey,
EncryptedReadKeys: ch.EncryptedReadKeys, Permissions: invite.Permissions,
Permissions: invite.Permissions,
IsConfirmed: true,
} }
st.userStates[ch.Identity] = userState st.userStates[chIdentity] = userState
return nil return nil
} }
func (st *ACLState) applyUserAdd(ch *aclpb.ACLChangeUserAdd) error { func (st *ACLState) applyUserAdd(ch *aclrecordproto.ACLUserAdd) error {
if _, exists := st.userStates[ch.Identity]; exists { chIdentity := string(ch.Identity)
if _, exists := st.userStates[chIdentity]; exists {
return ErrUserAlreadyExists return ErrUserAlreadyExists
} }
st.userStates[ch.Identity] = &aclpb.ACLChangeUserState{ st.userStates[chIdentity] = &aclrecordproto.ACLUserState{
Identity: ch.Identity, Identity: ch.Identity,
EncryptionKey: ch.EncryptionKey, EncryptionKey: ch.EncryptionKey,
Permissions: ch.Permissions, Permissions: ch.Permissions,
EncryptedReadKeys: ch.EncryptedReadKeys,
} }
if ch.Identity == st.identity { if chIdentity == st.identity {
for _, key := range ch.EncryptedReadKeys { for _, key := range ch.EncryptedReadKeys {
key, hash, err := st.decryptReadKeyAndHash(key) key, hash, err := st.decryptReadKeyAndHash(key)
if err != nil { if err != nil {
@ -304,26 +337,22 @@ func (st *ACLState) applyUserAdd(ch *aclpb.ACLChangeUserAdd) error {
return nil return nil
} }
func (st *ACLState) applyUserRemove(ch *aclpb.ACLChangeUserRemove) error { func (st *ACLState) applyUserRemove(ch *aclrecordproto.ACLUserRemove) error {
if ch.Identity == st.identity { chIdentity := string(ch.Identity)
if chIdentity == st.identity {
return ErrDocumentForbidden return ErrDocumentForbidden
} }
if _, exists := st.userStates[ch.Identity]; !exists { if _, exists := st.userStates[chIdentity]; !exists {
return ErrNoSuchUser return ErrNoSuchUser
} }
delete(st.userStates, ch.Identity) delete(st.userStates, chIdentity)
for _, replace := range ch.ReadKeyReplaces { for _, replace := range ch.ReadKeyReplaces {
userState, exists := st.userStates[replace.Identity] repIdentity := string(replace.Identity)
if !exists {
continue
}
userState.EncryptedReadKeys = append(userState.EncryptedReadKeys, replace.EncryptedReadKey)
// if this is our identity then we have to decrypt the key // if this is our identity then we have to decrypt the key
if replace.Identity == st.identity { if repIdentity == st.identity {
key, hash, err := st.decryptReadKeyAndHash(replace.EncryptedReadKey) key, hash, err := st.decryptReadKeyAndHash(replace.EncryptedReadKey)
if err != nil { if err != nil {
return ErrFailedToDecrypt return ErrFailedToDecrypt
@ -336,16 +365,6 @@ func (st *ACLState) applyUserRemove(ch *aclpb.ACLChangeUserRemove) error {
return nil return nil
} }
func (st *ACLState) applyUserConfirm(ch *aclpb.ACLChangeUserConfirm) error {
if _, exists := st.userStates[ch.Identity]; !exists {
return ErrNoSuchUser
}
userState := st.userStates[ch.Identity]
userState.IsConfirmed = true
return nil
}
func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) { func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, error) {
decrypted, err := st.encryptionKey.Decrypt(msg) decrypted, err := st.encryptionKey.Decrypt(msg)
if err != nil { if err != nil {
@ -362,8 +381,8 @@ func (st *ACLState) decryptReadKeyAndHash(msg []byte) (*symmetric.Key, uint64, e
return key, hasher.Sum64(), nil return key, hasher.Sum64(), nil
} }
func (st *ACLState) hasPermission(identity string, permission aclpb.ACLChangeUserPermissions) bool { func (st *ACLState) hasPermission(identity []byte, permission aclrecordproto.ACLUserPermissions) bool {
state, exists := st.userStates[identity] state, exists := st.userStates[string(identity)]
if !exists { if !exists {
return false return false
} }
@ -371,17 +390,17 @@ func (st *ACLState) hasPermission(identity string, permission aclpb.ACLChangeUse
return state.Permissions == permission return state.Permissions == permission
} }
func (st *ACLState) isUserJoin(data *aclpb.ACLChangeACLData) bool { func (st *ACLState) isUserJoin(data *aclrecordproto.ACLData) bool {
// if we have a UserJoin, then it should always be the first one applied // if we have a UserJoin, then it should always be the first one applied
return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil
} }
func (st *ACLState) isUserAdd(data *aclpb.ACLChangeACLData, identity string) bool { func (st *ACLState) isUserAdd(data *aclrecordproto.ACLData, identity []byte) bool {
// if we have a UserAdd, then it should always be the first one applied // if we have a UserAdd, then it should always be the first one applied
userAdd := data.GetAclContent()[0].GetUserAdd() userAdd := data.GetAclContent()[0].GetUserAdd()
return data.GetAclContent() != nil && userAdd != nil && userAdd.GetIdentity() == identity return data.GetAclContent() != nil && userAdd != nil && bytes.Compare(userAdd.GetIdentity(), identity) == 0
} }
func (st *ACLState) GetUserStates() map[string]*aclpb.ACLChangeUserState { func (st *ACLState) GetUserStates() map[string]*aclrecordproto.ACLUserState {
return st.userStates return st.userStates
} }

View File

@ -2,43 +2,42 @@ package list
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
) )
type aclStateBuilder struct { type aclStateBuilder struct {
identity string signPrivKey signingkey.PrivKey
key encryptionkey.PrivKey encPrivKey encryptionkey.PrivKey
decoder keys.Decoder id string
} }
func newACLStateBuilderWithIdentity(decoder keys.Decoder, accountData *account.AccountData) *aclStateBuilder { func newACLStateBuilderWithIdentity(accountData *account.AccountData) *aclStateBuilder {
return &aclStateBuilder{ return &aclStateBuilder{
decoder: decoder, signPrivKey: accountData.SignKey,
identity: accountData.Identity, encPrivKey: accountData.EncKey,
key: accountData.EncKey,
} }
} }
func newACLStateBuilder(decoder keys.Decoder) *aclStateBuilder { func newACLStateBuilder() *aclStateBuilder {
return &aclStateBuilder{ return &aclStateBuilder{}
decoder: decoder,
}
} }
func (sb *aclStateBuilder) Build(records []*Record) (*ACLState, error) { func (sb *aclStateBuilder) Init(id string) {
var ( sb.id = id
err error }
state *ACLState
)
if sb.key != nil { func (sb *aclStateBuilder) Build(records []*ACLRecord) (state *ACLState, err error) {
state = newACLStateWithIdentity(sb.identity, sb.key, sb.decoder) if sb.encPrivKey != nil && sb.signPrivKey != nil {
state, err = newACLStateWithKeys(sb.id, sb.signPrivKey, sb.encPrivKey)
if err != nil {
return
}
} else { } else {
state = newACLState(sb.decoder) state = newACLState(sb.id)
} }
for _, rec := range records { for _, rec := range records {
err = state.applyChangeAndUpdate(rec) err = state.applyRecord(rec)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,131 +0,0 @@
package list
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
"github.com/gogo/protobuf/proto"
"hash/fnv"
"time"
)
type MarshalledChange = []byte
type ACLChangeBuilder interface {
UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error
AddId(id string) // TODO: this is only for testing
}
type aclChangeBuilder struct {
aclState *ACLState
list ACLList
acc *account.AccountData
aclData *aclpb.ACLChangeACLData
id string
readKey *symmetric.Key
readKeyHash uint64
}
func newACLChangeBuilder() *aclChangeBuilder {
return &aclChangeBuilder{}
}
func (c *aclChangeBuilder) Init(state *ACLState, list ACLList, acc *account.AccountData) {
c.aclState = state
c.list = list
c.acc = acc
c.aclData = &aclpb.ACLChangeACLData{}
// setting read key for further encryption etc
if state.currentReadKeyHash == 0 {
c.readKey, _ = symmetric.NewRandom()
hasher := fnv.New64()
hasher.Write(c.readKey.Bytes())
c.readKeyHash = hasher.Sum64()
} else {
c.readKey = c.aclState.userReadKeys[c.aclState.currentReadKeyHash]
c.readKeyHash = c.aclState.currentReadKeyHash
}
}
func (c *aclChangeBuilder) AddId(id string) {
c.id = id
}
func (c *aclChangeBuilder) UserAdd(identity string, encryptionKey encryptionkey.PubKey, permissions aclpb.ACLChangeUserPermissions) error {
var allKeys []*symmetric.Key
if c.aclState.currentReadKeyHash != 0 {
for _, key := range c.aclState.userReadKeys {
allKeys = append(allKeys, key)
}
} else {
allKeys = append(allKeys, c.readKey)
}
var encryptedKeys [][]byte
for _, k := range allKeys {
res, err := encryptionKey.Encrypt(k.Bytes())
if err != nil {
return err
}
encryptedKeys = append(encryptedKeys, res)
}
rawKey, err := encryptionKey.Raw()
if err != nil {
return err
}
ch := &aclpb.ACLChangeACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{
UserAdd: &aclpb.ACLChangeUserAdd{
Identity: identity,
EncryptionKey: rawKey,
EncryptedReadKeys: encryptedKeys,
Permissions: permissions,
},
},
}
c.aclData.AclContent = append(c.aclData.AclContent, ch)
return nil
}
func (c *aclChangeBuilder) BuildAndApply() (*Record, []byte, error) {
aclRecord := &aclpb.Record{
PrevId: c.list.Head().Id,
CurrentReadKeyHash: c.readKeyHash,
Timestamp: int64(time.Now().Nanosecond()),
Identity: c.acc.Identity,
}
marshalledData, err := proto.Marshal(c.aclData)
if err != nil {
return nil, nil, err
}
aclRecord.Data = marshalledData
err = c.aclState.applyRecord(aclRecord)
if err != nil {
return nil, nil, err
}
fullMarshalledChange, err := proto.Marshal(aclRecord)
if err != nil {
return nil, nil, err
}
signature, err := c.acc.SignKey.Sign(fullMarshalledChange)
if err != nil {
return nil, nil, err
}
id, err := cid.NewCIDFromBytes(fullMarshalledChange)
if err != nil {
return nil, nil, err
}
ch := NewRecord(id, aclRecord)
ch.Model = c.aclData
ch.Sign = signature
return ch, fullMarshalledChange, nil
}

View File

@ -5,15 +5,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"sync" "sync"
) )
type IterFunc = func(record *Record) (IsContinue bool) type IterFunc = func(record *ACLRecord) (IsContinue bool)
var ErrIncorrectCID = errors.New("incorrect CID") var ErrIncorrectCID = errors.New("incorrect CID")
@ -26,20 +24,20 @@ type RWLocker interface {
type ACLList interface { type ACLList interface {
RWLocker RWLocker
ID() string ID() string
Header() *aclpb.Header Root() *aclrecordproto.ACLRoot
Records() []*Record Records() []*ACLRecord
ACLState() *ACLState ACLState() *ACLState
IsAfter(first string, second string) (bool, error) IsAfter(first string, second string) (bool, error)
Head() *Record Head() *ACLRecord
Get(id string) (*Record, error) Get(id string) (*ACLRecord, error)
Iterate(iterFunc IterFunc) Iterate(iterFunc IterFunc)
IterateFrom(startId string, iterFunc IterFunc) IterateFrom(startId string, iterFunc IterFunc)
Close() (err error) Close() (err error)
} }
type aclList struct { type aclList struct {
header *aclpb.Header root *aclrecordproto.ACLRoot
records []*Record records []*ACLRecord
indexes map[string]int indexes map[string]int
id string id string
@ -51,57 +49,57 @@ type aclList struct {
} }
func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) { func BuildACLListWithIdentity(acc *account.AccountData, storage storage.ListStorage) (ACLList, error) {
builder := newACLStateBuilderWithIdentity(acc.Decoder, acc)
return buildWithACLStateBuilder(builder, storage)
}
func BuildACLList(decoder keys.Decoder, storage storage.ListStorage) (ACLList, error) {
return buildWithACLStateBuilder(newACLStateBuilder(decoder), storage)
}
func buildWithACLStateBuilder(builder *aclStateBuilder, storage storage.ListStorage) (list ACLList, err error) {
header, err := storage.Header()
if err != nil {
return
}
id, err := storage.ID() id, err := storage.ID()
if err != nil {
return nil, err
}
builder := newACLStateBuilderWithIdentity(acc)
return build(id, builder, newACLRecordBuilder(id, common.NewKeychain()), storage)
}
func BuildACLList(storage storage.ListStorage) (ACLList, error) {
id, err := storage.ID()
if err != nil {
return nil, err
}
return build(id, newACLStateBuilder(), newACLRecordBuilder(id, common.NewKeychain()), storage)
}
func build(id string, stateBuilder *aclStateBuilder, recBuilder ACLRecordBuilder, storage storage.ListStorage) (list ACLList, err error) {
rootWithId, err := storage.Root()
if err != nil { if err != nil {
return return
} }
aclRecRoot, err := recBuilder.ConvertFromRaw(rootWithId)
rawRecord, err := storage.Head()
if err != nil { if err != nil {
return return
} }
keychain := common.NewKeychain() rawRecordWithId, err := storage.Head()
record, err := NewFromRawRecord(rawRecord)
if err != nil { if err != nil {
return return
} }
err = verifyRecord(keychain, rawRecord, record)
if err != nil {
return
}
records := []*Record{record}
for record.Content.PrevId != "" { record, err := recBuilder.ConvertFromRaw(rawRecordWithId)
rawRecord, err = storage.GetRawRecord(context.Background(), record.Content.PrevId) if err != nil {
return
}
records := []*ACLRecord{record}
for record.PrevId != "" && record.PrevId != id {
rawRecordWithId, err = storage.GetRawRecord(context.Background(), record.PrevId)
if err != nil { if err != nil {
return return
} }
record, err = NewFromRawRecord(rawRecord) record, err = recBuilder.ConvertFromRaw(rawRecordWithId)
if err != nil {
return
}
err = verifyRecord(keychain, rawRecord, record)
if err != nil { if err != nil {
return return
} }
records = append(records, record) records = append(records, record)
} }
// adding root in the end, because we already parsed it
records = append(records, aclRecRoot)
indexes := make(map[string]int) indexes := make(map[string]int)
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
@ -114,16 +112,17 @@ func buildWithACLStateBuilder(builder *aclStateBuilder, storage storage.ListStor
indexes[records[len(records)/2].Id] = len(records) / 2 indexes[records[len(records)/2].Id] = len(records) / 2
} }
state, err := builder.Build(records) stateBuilder.Init(id)
state, err := stateBuilder.Build(records)
if err != nil { if err != nil {
return return
} }
list = &aclList{ list = &aclList{
header: header, root: aclRecRoot.Model.(*aclrecordproto.ACLRoot),
records: records, records: records,
indexes: indexes, indexes: indexes,
builder: builder, builder: stateBuilder,
aclState: state, aclState: state,
id: id, id: id,
RWMutex: sync.RWMutex{}, RWMutex: sync.RWMutex{},
@ -131,7 +130,7 @@ func buildWithACLStateBuilder(builder *aclStateBuilder, storage storage.ListStor
return return
} }
func (a *aclList) Records() []*Record { func (a *aclList) Records() []*ACLRecord {
return a.records return a.records
} }
@ -139,8 +138,8 @@ func (a *aclList) ID() string {
return a.id return a.id
} }
func (a *aclList) Header() *aclpb.Header { func (a *aclList) Root() *aclrecordproto.ACLRoot {
return a.header return a.root
} }
func (a *aclList) ACLState() *ACLState { func (a *aclList) ACLState() *ACLState {
@ -156,11 +155,11 @@ func (a *aclList) IsAfter(first string, second string) (bool, error) {
return firstRec >= secondRec, nil return firstRec >= secondRec, nil
} }
func (a *aclList) Head() *Record { func (a *aclList) Head() *ACLRecord {
return a.records[len(a.records)-1] return a.records[len(a.records)-1]
} }
func (a *aclList) Get(id string) (*Record, error) { func (a *aclList) Get(id string) (*ACLRecord, error) {
recIdx, ok := a.indexes[id] recIdx, ok := a.indexes[id]
if !ok { if !ok {
return nil, fmt.Errorf("no such record") return nil, fmt.Errorf("no such record")
@ -191,26 +190,3 @@ func (a *aclList) IterateFrom(startId string, iterFunc IterFunc) {
func (a *aclList) Close() (err error) { func (a *aclList) Close() (err error) {
return nil return nil
} }
func verifyRecord(keychain *common.Keychain, rawRecord *aclpb.RawRecord, record *Record) (err error) {
identityKey, err := keychain.GetOrAdd(record.Content.Identity)
if err != nil {
return
}
// verifying signature
res, err := identityKey.Verify(rawRecord.Payload, rawRecord.Signature)
if err != nil {
return
}
if !res {
err = ErrInvalidSignature
return
}
// verifying ID
if !cid.VerifyCID(rawRecord.Payload, rawRecord.Id) {
err = ErrIncorrectCID
}
return
}

View File

@ -1,9 +1,8 @@
package list package list
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"testing" "testing"
@ -15,7 +14,7 @@ func TestAclList_ACLState_UserInviteAndJoin(t *testing.T) {
keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain() keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain()
aclList, err := BuildACLList(signingkey.NewEDPubKeyDecoder(), st) aclList, err := BuildACLList(st)
require.NoError(t, err, "building acl list should be without error") require.NoError(t, err, "building acl list should be without error")
idA := keychain.GetIdentity("A") idA := keychain.GetIdentity("A")
@ -23,13 +22,13 @@ func TestAclList_ACLState_UserInviteAndJoin(t *testing.T) {
idC := keychain.GetIdentity("C") idC := keychain.GetIdentity("C")
// checking final state // checking final state
assert.Equal(t, aclpb.ACLChange_Admin, aclList.ACLState().GetUserStates()[idA].Permissions) assert.Equal(t, aclrecordproto.ACLUserPermissions_Admin, aclList.ACLState().GetUserStates()[idA].Permissions)
assert.Equal(t, aclpb.ACLChange_Writer, aclList.ACLState().GetUserStates()[idB].Permissions) assert.Equal(t, aclrecordproto.ACLUserPermissions_Writer, aclList.ACLState().GetUserStates()[idB].Permissions)
assert.Equal(t, aclpb.ACLChange_Reader, aclList.ACLState().GetUserStates()[idC].Permissions) assert.Equal(t, aclrecordproto.ACLUserPermissions_Reader, aclList.ACLState().GetUserStates()[idC].Permissions)
assert.Equal(t, aclList.Head().Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash())
var records []*Record var records []*ACLRecord
aclList.Iterate(func(record *Record) (IsContinue bool) { aclList.Iterate(func(record *ACLRecord) (IsContinue bool) {
records = append(records, record) records = append(records, record)
return true return true
}) })
@ -44,7 +43,7 @@ func TestAclList_ACLState_UserInviteAndJoin(t *testing.T) {
assert.NoError(t, err, "should have no error with permissions of B in the record 2") assert.NoError(t, err, "should have no error with permissions of B in the record 2")
assert.Equal(t, UserPermissionPair{ assert.Equal(t, UserPermissionPair{
Identity: idB, Identity: idB,
Permission: aclpb.ACLChange_Writer, Permission: aclrecordproto.ACLUserPermissions_Writer,
}, perm) }, perm)
} }
@ -54,7 +53,7 @@ func TestAclList_ACLState_UserJoinAndRemove(t *testing.T) {
keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain() keychain := st.(*acllistbuilder.ACLListStorageBuilder).GetKeychain()
aclList, err := BuildACLList(signingkey.NewEDPubKeyDecoder(), st) aclList, err := BuildACLList(st)
require.NoError(t, err, "building acl list should be without error") require.NoError(t, err, "building acl list should be without error")
idA := keychain.GetIdentity("A") idA := keychain.GetIdentity("A")
@ -62,15 +61,15 @@ func TestAclList_ACLState_UserJoinAndRemove(t *testing.T) {
idC := keychain.GetIdentity("C") idC := keychain.GetIdentity("C")
// checking final state // checking final state
assert.Equal(t, aclpb.ACLChange_Admin, aclList.ACLState().GetUserStates()[idA].Permissions) assert.Equal(t, aclrecordproto.ACLUserPermissions_Admin, aclList.ACLState().GetUserStates()[idA].Permissions)
assert.Equal(t, aclpb.ACLChange_Reader, aclList.ACLState().GetUserStates()[idC].Permissions) assert.Equal(t, aclrecordproto.ACLUserPermissions_Reader, aclList.ACLState().GetUserStates()[idC].Permissions)
assert.Equal(t, aclList.Head().Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) assert.Equal(t, aclList.Head().CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash())
_, exists := aclList.ACLState().GetUserStates()[idB] _, exists := aclList.ACLState().GetUserStates()[idB]
assert.Equal(t, false, exists) assert.Equal(t, false, exists)
var records []*Record var records []*ACLRecord
aclList.Iterate(func(record *Record) (IsContinue bool) { aclList.Iterate(func(record *ACLRecord) (IsContinue bool) {
records = append(records, record) records = append(records, record)
return true return true
}) })
@ -78,13 +77,13 @@ func TestAclList_ACLState_UserJoinAndRemove(t *testing.T) {
// checking permissions at specific records // checking permissions at specific records
assert.Equal(t, 4, len(records)) assert.Equal(t, 4, len(records))
assert.NotEqual(t, records[2].Content.CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash()) assert.NotEqual(t, records[2].CurrentReadKeyHash, aclList.ACLState().CurrentReadKeyHash())
perm, err := aclList.ACLState().PermissionsAtRecord(records[2].Id, idB) perm, err := aclList.ACLState().PermissionsAtRecord(records[2].Id, idB)
assert.NoError(t, err, "should have no error with permissions of B in the record 2") assert.NoError(t, err, "should have no error with permissions of B in the record 2")
assert.Equal(t, UserPermissionPair{ assert.Equal(t, UserPermissionPair{
Identity: idB, Identity: idB,
Permission: aclpb.ACLChange_Writer, Permission: aclrecordproto.ACLUserPermissions_Writer,
}, perm) }, perm)
_, err = aclList.ACLState().PermissionsAtRecord(records[3].Id, idB) _, err = aclList.ACLState().PermissionsAtRecord(records[3].Id, idB)

View File

@ -1,34 +1,12 @@
package list package list
import ( type ACLRecord struct {
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" Id string
"github.com/gogo/protobuf/proto" PrevId string
) CurrentReadKeyHash uint64
Timestamp int64
type Record struct { Data []byte
Id string Identity []byte
Content *aclpb.Record Model interface{}
Model interface{} Signature []byte
Sign []byte
}
func NewRecord(id string, aclRecord *aclpb.Record) *Record {
return &Record{
Id: id,
Content: aclRecord,
}
}
func NewFromRawRecord(rawRec *aclpb.RawRecord) (*Record, error) {
aclRec := &aclpb.Record{}
err := proto.Unmarshal(rawRec.Payload, aclRec)
if err != nil {
return nil, err
}
return &Record{
Id: rawRec.Id,
Content: aclRec,
Sign: rawRec.Signature,
}, nil
} }

View File

@ -3,44 +3,41 @@ package storage
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"sync" "sync"
) )
type inMemoryACLListStorage struct { type inMemoryACLListStorage struct {
header *aclpb.Header records []*aclrecordproto.RawACLRecordWithId
records []*aclpb.RawRecord id string
id string
sync.RWMutex sync.RWMutex
} }
func NewInMemoryACLListStorage( func NewInMemoryACLListStorage(
id string, id string,
header *aclpb.Header, records []*aclrecordproto.RawACLRecordWithId) (ListStorage, error) {
records []*aclpb.RawRecord) (ListStorage, error) {
return &inMemoryACLListStorage{ return &inMemoryACLListStorage{
id: id, id: id,
header: header,
records: records, records: records,
RWMutex: sync.RWMutex{}, RWMutex: sync.RWMutex{},
}, nil }, nil
} }
func (i *inMemoryACLListStorage) Header() (*aclpb.Header, error) { func (i *inMemoryACLListStorage) Root() (*aclrecordproto.RawACLRecordWithId, error) {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
return i.header, nil return i.records[0], nil
} }
func (i *inMemoryACLListStorage) Head() (*aclpb.RawRecord, error) { func (i *inMemoryACLListStorage) Head() (*aclrecordproto.RawACLRecordWithId, error) {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
return i.records[len(i.records)-1], nil return i.records[len(i.records)-1], nil
} }
func (i *inMemoryACLListStorage) GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) { func (i *inMemoryACLListStorage) GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawACLRecordWithId, error) {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
for _, rec := range i.records { for _, rec := range i.records {
@ -51,7 +48,7 @@ func (i *inMemoryACLListStorage) GetRawRecord(ctx context.Context, id string) (*
return nil, fmt.Errorf("no such record") return nil, fmt.Errorf("no such record")
} }
func (i *inMemoryACLListStorage) AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error { func (i *inMemoryACLListStorage) AddRawRecord(ctx context.Context, rec *aclrecordproto.RawACLRecordWithId) error {
panic("implement me") panic("implement me")
} }
@ -63,26 +60,27 @@ func (i *inMemoryACLListStorage) ID() (string, error) {
type inMemoryTreeStorage struct { type inMemoryTreeStorage struct {
id string id string
header *aclpb.Header root *treechangeproto.RawTreeChangeWithId
heads []string heads []string
changes map[string]*aclpb.RawChange changes map[string]*treechangeproto.RawTreeChangeWithId
sync.RWMutex sync.RWMutex
} }
func NewInMemoryTreeStorage( func NewInMemoryTreeStorage(
treeId string, treeId string,
header *aclpb.Header, root *treechangeproto.RawTreeChangeWithId,
heads []string, heads []string,
changes []*aclpb.RawChange) (TreeStorage, error) { changes []*treechangeproto.RawTreeChangeWithId) (TreeStorage, error) {
allChanges := make(map[string]*aclpb.RawChange) allChanges := make(map[string]*treechangeproto.RawTreeChangeWithId)
for _, ch := range changes { for _, ch := range changes {
allChanges[ch.Id] = ch allChanges[ch.Id] = ch
} }
allChanges[treeId] = root
return &inMemoryTreeStorage{ return &inMemoryTreeStorage{
id: treeId, id: treeId,
header: header, root: root,
heads: heads, heads: heads,
changes: allChanges, changes: allChanges,
RWMutex: sync.RWMutex{}, RWMutex: sync.RWMutex{},
@ -95,10 +93,10 @@ func (t *inMemoryTreeStorage) ID() (string, error) {
return t.id, nil return t.id, nil
} }
func (t *inMemoryTreeStorage) Header() (*aclpb.Header, error) { func (t *inMemoryTreeStorage) Root() (*treechangeproto.RawTreeChangeWithId, error) {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
return t.header, nil return t.root, nil
} }
func (t *inMemoryTreeStorage) Heads() ([]string, error) { func (t *inMemoryTreeStorage) Heads() ([]string, error) {
@ -118,7 +116,7 @@ func (t *inMemoryTreeStorage) SetHeads(heads []string) error {
return nil return nil
} }
func (t *inMemoryTreeStorage) AddRawChange(change *aclpb.RawChange) error { func (t *inMemoryTreeStorage) AddRawChange(change *treechangeproto.RawTreeChangeWithId) error {
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
// TODO: better to do deep copy // TODO: better to do deep copy
@ -126,7 +124,7 @@ func (t *inMemoryTreeStorage) AddRawChange(change *aclpb.RawChange) error {
return nil return nil
} }
func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string) (*aclpb.RawChange, error) { func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string) (*treechangeproto.RawTreeChangeWithId, error) {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
if res, exists := t.changes[changeId]; exists { if res, exists := t.changes[changeId]; exists {
@ -136,22 +134,11 @@ func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string)
} }
type inMemoryStorageProvider struct { type inMemoryStorageProvider struct {
objects map[string]Storage objects map[string]TreeStorage
sync.RWMutex sync.RWMutex
} }
func (i *inMemoryStorageProvider) AddStorage(id string, st Storage) error { func (i *inMemoryStorageProvider) TreeStorage(id string) (TreeStorage, error) {
i.Lock()
defer i.Unlock()
if _, exists := i.objects[id]; exists {
return fmt.Errorf("storage already exists")
}
i.objects[id] = st
return nil
}
func (i *inMemoryStorageProvider) Storage(id string) (Storage, error) {
i.RLock() i.RLock()
defer i.RUnlock() defer i.RUnlock()
if tree, exists := i.objects[id]; exists { if tree, exists := i.objects[id]; exists {
@ -163,7 +150,7 @@ func (i *inMemoryStorageProvider) Storage(id string) (Storage, error) {
func (i *inMemoryStorageProvider) CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) { func (i *inMemoryStorageProvider) CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) {
i.Lock() i.Lock()
defer i.Unlock() defer i.Unlock()
res, err := NewInMemoryTreeStorage(payload.TreeId, payload.Header, payload.Heads, payload.Changes) res, err := NewInMemoryTreeStorage(payload.TreeId, payload.RootRawChange, payload.Heads, payload.Changes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -172,20 +159,8 @@ func (i *inMemoryStorageProvider) CreateTreeStorage(payload TreeStorageCreatePay
return res, nil return res, nil
} }
func (i *inMemoryStorageProvider) CreateACLListStorage(payload ACLListStorageCreatePayload) (ListStorage, error) {
i.Lock()
defer i.Unlock()
res, err := NewInMemoryACLListStorage(payload.ListId, payload.Header, payload.Records)
if err != nil {
return nil, err
}
i.objects[payload.ListId] = res
return res, nil
}
func NewInMemoryTreeStorageProvider() Provider { func NewInMemoryTreeStorageProvider() Provider {
return &inMemoryStorageProvider{ return &inMemoryStorageProvider{
objects: make(map[string]Storage), objects: make(map[string]TreeStorage),
} }
} }

View File

@ -2,13 +2,14 @@ package storage
import ( import (
"context" "context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
) )
type ListStorage interface { type ListStorage interface {
Storage Storage
Head() (*aclpb.RawRecord, error) Root() (*aclrecordproto.RawACLRecordWithId, error)
Head() (*aclrecordproto.RawACLRecordWithId, error)
GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawACLRecordWithId, error)
AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error AddRawRecord(ctx context.Context, rec *aclrecordproto.RawACLRecordWithId) error
} }

View File

@ -2,27 +2,19 @@ package storage
import ( import (
"errors" "errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
) )
var ErrUnknownTreeId = errors.New("tree does not exist") var ErrUnknownTreeId = errors.New("tree does not exist")
type TreeStorageCreatePayload struct { type TreeStorageCreatePayload struct {
TreeId string TreeId string
Header *aclpb.Header RootRawChange *treechangeproto.RawTreeChangeWithId
Changes []*aclpb.RawChange Changes []*treechangeproto.RawTreeChangeWithId
Heads []string Heads []string
}
type ACLListStorageCreatePayload struct {
ListId string
Header *aclpb.Header
Records []*aclpb.RawRecord
} }
type Provider interface { type Provider interface {
Storage(id string) (Storage, error) TreeStorage(id string) (TreeStorage, error)
AddStorage(id string, st Storage) error
CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error) CreateTreeStorage(payload TreeStorageCreatePayload) (TreeStorage, error)
CreateACLListStorage(payload ACLListStorageCreatePayload) (ListStorage, error)
} }

View File

@ -1,8 +1,5 @@
package storage package storage
import "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
type Storage interface { type Storage interface {
ID() (string, error) ID() (string, error)
Header() (*aclpb.Header, error)
} }

View File

@ -2,16 +2,17 @@ package storage
import ( import (
"context" "context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
) )
type TreeStorage interface { type TreeStorage interface {
Storage Storage
Root() (*treechangeproto.RawTreeChangeWithId, error)
Heads() ([]string, error) Heads() ([]string, error)
SetHeads(heads []string) error SetHeads(heads []string) error
AddRawChange(change *aclpb.RawChange) error AddRawChange(change *treechangeproto.RawTreeChangeWithId) error
GetRawChange(ctx context.Context, recordID string) (*aclpb.RawChange, error) GetRawChange(ctx context.Context, id string) (*treechangeproto.RawTreeChangeWithId, error)
} }
type TreeStorageCreatorFunc = func(payload TreeStorageCreatePayload) (TreeStorage, error) type TreeStorageCreatorFunc = func(payload TreeStorageCreatePayload) (TreeStorage, error)

View File

@ -1,6 +1,8 @@
package acllistbuilder package acllistbuilder
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"hash/fnv" "hash/fnv"
@ -14,29 +16,29 @@ type SymKey struct {
Key *symmetric.Key Key *symmetric.Key
} }
type Keychain struct { type YAMLKeychain struct {
SigningKeys map[string]signingkey.PrivKey SigningKeysByYAMLIdentity map[string]signingkey.PrivKey
SigningKeysByIdentity map[string]signingkey.PrivKey SigningKeysByRealIdentity map[string]signingkey.PrivKey
EncryptionKeys map[string]encryptionkey.PrivKey EncryptionKeysByYAMLIdentity map[string]encryptionkey.PrivKey
ReadKeys map[string]*SymKey ReadKeysByYAMLIdentity map[string]*SymKey
ReadKeysByHash map[uint64]*SymKey ReadKeysByHash map[uint64]*SymKey
GeneratedIdentities map[string]string GeneratedIdentities map[string]string
coder signingkey.PubKeyDecoder DerivedIdentity string
} }
func NewKeychain() *Keychain { func NewKeychain() *YAMLKeychain {
return &Keychain{ return &YAMLKeychain{
SigningKeys: map[string]signingkey.PrivKey{}, SigningKeysByYAMLIdentity: map[string]signingkey.PrivKey{},
SigningKeysByIdentity: map[string]signingkey.PrivKey{}, SigningKeysByRealIdentity: map[string]signingkey.PrivKey{},
EncryptionKeys: map[string]encryptionkey.PrivKey{}, EncryptionKeysByYAMLIdentity: map[string]encryptionkey.PrivKey{},
GeneratedIdentities: map[string]string{}, GeneratedIdentities: map[string]string{},
ReadKeys: map[string]*SymKey{}, ReadKeysByYAMLIdentity: map[string]*SymKey{},
ReadKeysByHash: map[uint64]*SymKey{}, ReadKeysByHash: map[uint64]*SymKey{},
coder: signingkey.NewEd25519PubKeyDecoder(),
} }
} }
func (k *Keychain) ParseKeys(keys *Keys) { func (k *YAMLKeychain) ParseKeys(keys *Keys) {
k.DerivedIdentity = keys.Derived
for _, encKey := range keys.Enc { for _, encKey := range keys.Enc {
k.AddEncryptionKey(encKey) k.AddEncryptionKey(encKey)
} }
@ -50,8 +52,8 @@ func (k *Keychain) ParseKeys(keys *Keys) {
} }
} }
func (k *Keychain) AddEncryptionKey(key *Key) { func (k *YAMLKeychain) AddEncryptionKey(key *Key) {
if _, exists := k.EncryptionKeys[key.Name]; exists { if _, exists := k.EncryptionKeysByYAMLIdentity[key.Name]; exists {
return return
} }
var ( var (
@ -64,18 +66,16 @@ func (k *Keychain) AddEncryptionKey(key *Key) {
panic(err) panic(err)
} }
} else { } else {
decoder := encryptionkey.NewRSAPrivKeyDecoder() newPrivKey, err = keys.DecodeKeyFromString(key.Value, encryptionkey.NewEncryptionRsaPrivKeyFromBytes, nil)
privKey, err := decoder.DecodeFromString(key.Value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
newPrivKey = privKey.(encryptionkey.PrivKey)
} }
k.EncryptionKeys[key.Name] = newPrivKey k.EncryptionKeysByYAMLIdentity[key.Name] = newPrivKey
} }
func (k *Keychain) AddSigningKey(key *Key) { func (k *YAMLKeychain) AddSigningKey(key *Key) {
if _, exists := k.SigningKeys[key.Name]; exists { if _, exists := k.SigningKeysByYAMLIdentity[key.Name]; exists {
return return
} }
var ( var (
@ -89,26 +89,26 @@ func (k *Keychain) AddSigningKey(key *Key) {
panic(err) panic(err)
} }
} else { } else {
decoder := signingkey.NewEDPrivKeyDecoder() newPrivKey, err = keys.DecodeKeyFromString(key.Value, signingkey.NewSigningEd25519PrivKeyFromBytes, nil)
privKey, err := decoder.DecodeFromString(key.Value)
if err != nil { if err != nil {
panic(err) panic(err)
} }
newPrivKey = privKey.(signingkey.PrivKey)
pubKey = newPrivKey.GetPublic() pubKey = newPrivKey.GetPublic()
} }
k.SigningKeys[key.Name] = newPrivKey k.SigningKeysByYAMLIdentity[key.Name] = newPrivKey
res, err := k.coder.EncodeToString(pubKey) rawPubKey, err := pubKey.Raw()
if err != nil { if err != nil {
panic(err) panic(err)
} }
k.SigningKeysByIdentity[res] = newPrivKey encoded := string(rawPubKey)
k.GeneratedIdentities[key.Name] = res
k.SigningKeysByRealIdentity[encoded] = newPrivKey
k.GeneratedIdentities[key.Name] = encoded
} }
func (k *Keychain) AddReadKey(key *Key) { func (k *YAMLKeychain) AddReadKey(key *Key) {
if _, exists := k.ReadKeys[key.Name]; exists { if _, exists := k.ReadKeysByYAMLIdentity[key.Name]; exists {
return return
} }
@ -121,6 +121,13 @@ func (k *Keychain) AddReadKey(key *Key) {
if err != nil { if err != nil {
panic("should be able to generate symmetric key") panic("should be able to generate symmetric key")
} }
} else if key.Value == "derived" {
signKey, _ := k.SigningKeysByYAMLIdentity[k.DerivedIdentity].Raw()
encKey, _ := k.EncryptionKeysByYAMLIdentity[k.DerivedIdentity].Raw()
rkey, err = aclrecordproto.ACLReadKeyDerive(signKey, encKey)
if err != nil {
panic("should be able to derive symmetric key")
}
} else { } else {
rkey, err = symmetric.FromString(key.Value) rkey, err = symmetric.FromString(key.Value)
if err != nil { if err != nil {
@ -131,7 +138,7 @@ func (k *Keychain) AddReadKey(key *Key) {
hasher := fnv.New64() hasher := fnv.New64()
hasher.Write(rkey.Bytes()) hasher.Write(rkey.Bytes())
k.ReadKeys[key.Name] = &SymKey{ k.ReadKeysByYAMLIdentity[key.Name] = &SymKey{
Hash: hasher.Sum64(), Hash: hasher.Sum64(),
Key: rkey, Key: rkey,
} }
@ -141,14 +148,14 @@ func (k *Keychain) AddReadKey(key *Key) {
} }
} }
func (k *Keychain) AddKey(key *Key) { func (k *YAMLKeychain) AddKey(key *Key) {
parts := strings.Split(key.Name, ".") parts := strings.Split(key.Name, ".")
if len(parts) != 3 { if len(parts) != 3 {
panic("cannot parse a key") panic("cannot parse a key")
} }
switch parts[1] { switch parts[1] {
case "Sign": case "Signature":
k.AddSigningKey(key) k.AddSigningKey(key)
case "Enc": case "Enc":
k.AddEncryptionKey(key) k.AddEncryptionKey(key)
@ -159,7 +166,7 @@ func (k *Keychain) AddKey(key *Key) {
} }
} }
func (k *Keychain) GetKey(key string) interface{} { func (k *YAMLKeychain) GetKey(key string) interface{} {
parts := strings.Split(key, ".") parts := strings.Split(key, ".")
if len(parts) != 3 { if len(parts) != 3 {
panic("cannot parse a key") panic("cannot parse a key")
@ -168,15 +175,15 @@ func (k *Keychain) GetKey(key string) interface{} {
switch parts[1] { switch parts[1] {
case "Sign": case "Sign":
if key, exists := k.SigningKeys[name]; exists { if key, exists := k.SigningKeysByYAMLIdentity[name]; exists {
return key return key
} }
case "Enc": case "Enc":
if key, exists := k.EncryptionKeys[name]; exists { if key, exists := k.EncryptionKeysByYAMLIdentity[name]; exists {
return key return key
} }
case "Read": case "Read":
if key, exists := k.ReadKeys[name]; exists { if key, exists := k.ReadKeysByYAMLIdentity[name]; exists {
return key return key
} }
default: default:
@ -185,6 +192,6 @@ func (k *Keychain) GetKey(key string) interface{} {
return nil return nil
} }
func (k *Keychain) GetIdentity(name string) string { func (k *YAMLKeychain) GetIdentity(name string) string {
return k.GeneratedIdentities[name] return k.GeneratedIdentities[name]
} }

View File

@ -3,12 +3,13 @@ package acllistbuilder
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/yamltests"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"hash/fnv"
"io/ioutil" "io/ioutil"
"path" "path"
"time" "time"
@ -19,17 +20,18 @@ import (
type ACLListStorageBuilder struct { type ACLListStorageBuilder struct {
aclList string aclList string
records []*aclpb.Record records []*aclrecordproto.ACLRecord
rawRecords []*aclpb.RawRecord rawRecords []*aclrecordproto.RawACLRecordWithId
indexes map[string]int indexes map[string]int
keychain *Keychain keychain *YAMLKeychain
header *aclpb.Header rawRoot *aclrecordproto.RawACLRecordWithId
root *aclrecordproto.ACLRoot
id string id string
} }
func NewACLListStorageBuilder(keychain *Keychain) *ACLListStorageBuilder { func NewACLListStorageBuilder(keychain *YAMLKeychain) *ACLListStorageBuilder {
return &ACLListStorageBuilder{ return &ACLListStorageBuilder{
records: make([]*aclpb.Record, 0), records: make([]*aclrecordproto.ACLRecord, 0),
indexes: make(map[string]int), indexes: make(map[string]int),
keychain: keychain, keychain: keychain,
} }
@ -58,47 +60,59 @@ func NewACLListStorageBuilderFromFile(file string) (*ACLListStorageBuilder, erro
return tb, nil return tb, nil
} }
func (t *ACLListStorageBuilder) createRaw(rec *aclpb.Record) *aclpb.RawRecord { func (t *ACLListStorageBuilder) createRaw(rec proto.Marshaler, identity []byte) *aclrecordproto.RawACLRecordWithId {
aclMarshaled, err := proto.Marshal(rec) protoMarshalled, err := rec.Marshal()
if err != nil { if err != nil {
panic("should be able to marshal final acl message!") panic("should be able to marshal final acl message!")
} }
signature, err := t.keychain.SigningKeysByIdentity[rec.Identity].Sign(aclMarshaled) signature, err := t.keychain.SigningKeysByRealIdentity[string(identity)].Sign(protoMarshalled)
if err != nil { if err != nil {
panic("should be able to sign final acl message!") panic("should be able to sign final acl message!")
} }
id, _ := cid.NewCIDFromBytes(aclMarshaled) rawRec := &aclrecordproto.RawACLRecord{
Payload: protoMarshalled,
return &aclpb.RawRecord{
Payload: aclMarshaled,
Signature: signature, Signature: signature,
Id: id, }
rawMarshalled, err := proto.Marshal(rawRec)
if err != nil {
panic(err)
}
id, _ := cid.NewCIDFromBytes(rawMarshalled)
return &aclrecordproto.RawACLRecordWithId{
Payload: rawMarshalled,
Id: id,
} }
} }
func (t *ACLListStorageBuilder) getRecord(idx int) *aclpb.RawRecord { func (t *ACLListStorageBuilder) Head() (*aclrecordproto.RawACLRecordWithId, error) {
return t.rawRecords[idx] l := len(t.records)
if l > 0 {
return t.rawRecords[l-1], nil
}
return t.rawRoot, nil
} }
func (t *ACLListStorageBuilder) Head() (*aclpb.RawRecord, error) { func (t *ACLListStorageBuilder) Root() (*aclrecordproto.RawACLRecordWithId, error) {
return t.getRecord(len(t.records) - 1), nil return t.rawRoot, nil
} }
func (t *ACLListStorageBuilder) Header() (*aclpb.Header, error) { func (t *ACLListStorageBuilder) GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawACLRecordWithId, error) {
return t.header, nil
}
func (t *ACLListStorageBuilder) GetRawRecord(ctx context.Context, id string) (*aclpb.RawRecord, error) {
recIdx, ok := t.indexes[id] recIdx, ok := t.indexes[id]
if !ok { if !ok {
if id == t.rawRoot.Id {
return t.rawRoot, nil
}
return nil, fmt.Errorf("no such record") return nil, fmt.Errorf("no such record")
} }
return t.getRecord(recIdx), nil return t.rawRecords[recIdx], nil
} }
func (t *ACLListStorageBuilder) AddRawRecord(ctx context.Context, rec *aclpb.RawRecord) error { func (t *ACLListStorageBuilder) AddRawRecord(ctx context.Context, rec *aclrecordproto.RawACLRecordWithId) error {
panic("implement me") panic("implement me")
} }
@ -106,11 +120,11 @@ func (t *ACLListStorageBuilder) ID() (string, error) {
return t.id, nil return t.id, nil
} }
func (t *ACLListStorageBuilder) GetRawRecords() []*aclpb.RawRecord { func (t *ACLListStorageBuilder) GetRawRecords() []*aclrecordproto.RawACLRecordWithId {
return t.rawRecords return t.rawRecords
} }
func (t *ACLListStorageBuilder) GetKeychain() *Keychain { func (t *ACLListStorageBuilder) GetKeychain() *YAMLKeychain {
return t.keychain return t.keychain
} }
@ -119,41 +133,40 @@ func (t *ACLListStorageBuilder) Parse(tree *YMLList) {
// are specified in the yml file, because our identities should be Ed25519 // are specified in the yml file, because our identities should be Ed25519
// the same thing is happening for the encryption keys // the same thing is happening for the encryption keys
t.keychain.ParseKeys(&tree.Keys) t.keychain.ParseKeys(&tree.Keys)
prevId := "" t.parseRoot(tree.Root)
prevId := t.id
for idx, rec := range tree.Records { for idx, rec := range tree.Records {
newRecord := t.parseRecord(rec, prevId) newRecord := t.parseRecord(rec, prevId)
rawRecord := t.createRaw(newRecord) rawRecord := t.createRaw(newRecord, newRecord.Identity)
t.records = append(t.records, newRecord) t.records = append(t.records, newRecord)
t.rawRecords = append(t.rawRecords, t.createRaw(newRecord)) t.rawRecords = append(t.rawRecords, rawRecord)
t.indexes[rawRecord.Id] = idx t.indexes[rawRecord.Id] = idx
prevId = rawRecord.Id prevId = rawRecord.Id
} }
t.createHeaderAndId()
} }
func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclpb.Record { func (t *ACLListStorageBuilder) parseRecord(rec *Record, prevId string) *aclrecordproto.ACLRecord {
k := t.keychain.GetKey(rec.ReadKey).(*SymKey) k := t.keychain.GetKey(rec.ReadKey).(*SymKey)
var aclChangeContents []*aclpb.ACLChangeACLContentValue var aclChangeContents []*aclrecordproto.ACLContentValue
for _, ch := range rec.AclChanges { for _, ch := range rec.AclChanges {
aclChangeContent := t.parseACLChange(ch) aclChangeContent := t.parseACLChange(ch)
aclChangeContents = append(aclChangeContents, aclChangeContent) aclChangeContents = append(aclChangeContents, aclChangeContent)
} }
data := &aclpb.ACLChangeACLData{ data := &aclrecordproto.ACLData{
AclContent: aclChangeContents, AclContent: aclChangeContents,
} }
bytes, _ := data.Marshal() bytes, _ := data.Marshal()
return &aclpb.Record{ return &aclrecordproto.ACLRecord{
PrevId: prevId, PrevId: prevId,
Identity: t.keychain.GetIdentity(rec.Identity), Identity: []byte(t.keychain.GetIdentity(rec.Identity)),
Data: bytes, Data: bytes,
CurrentReadKeyHash: k.Hash, CurrentReadKeyHash: k.Hash,
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
} }
} }
func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACLChangeACLContentValue) { func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclrecordproto.ACLContentValue) {
switch { switch {
case ch.UserAdd != nil: case ch.UserAdd != nil:
add := ch.UserAdd add := ch.UserAdd
@ -161,10 +174,10 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACL
encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey) encKey := t.keychain.GetKey(add.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw() rawKey, _ := encKey.GetPublic().Raw()
convCh = &aclpb.ACLChangeACLContentValue{ convCh = &aclrecordproto.ACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserAdd{ Value: &aclrecordproto.ACLContentValue_UserAdd{
UserAdd: &aclpb.ACLChangeUserAdd{ UserAdd: &aclrecordproto.ACLUserAdd{
Identity: t.keychain.GetIdentity(add.Identity), Identity: []byte(t.keychain.GetIdentity(add.Identity)),
EncryptionKey: rawKey, EncryptionKey: rawKey,
EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey), EncryptedReadKeys: t.encryptReadKeys(add.EncryptedReadKeys, encKey),
Permissions: t.convertPermission(add.Permission), Permissions: t.convertPermission(add.Permission),
@ -178,20 +191,20 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACL
GetKey(join.EncryptionKey).(encryptionkey.PrivKey) GetKey(join.EncryptionKey).(encryptionkey.PrivKey)
rawKey, _ := encKey.GetPublic().Raw() rawKey, _ := encKey.GetPublic().Raw()
idKey, _ := t.keychain.SigningKeys[join.Identity].GetPublic().Raw() idKey, _ := t.keychain.SigningKeysByYAMLIdentity[join.Identity].GetPublic().Raw()
signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey) signKey := t.keychain.GetKey(join.AcceptSignature).(signingkey.PrivKey)
signature, err := signKey.Sign(idKey) signature, err := signKey.Sign(idKey)
if err != nil { if err != nil {
panic(err) panic(err)
} }
convCh = &aclpb.ACLChangeACLContentValue{ convCh = &aclrecordproto.ACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserJoin{ Value: &aclrecordproto.ACLContentValue_UserJoin{
UserJoin: &aclpb.ACLChangeUserJoin{ UserJoin: &aclrecordproto.ACLUserJoin{
Identity: t.keychain.GetIdentity(join.Identity), Identity: []byte(t.keychain.GetIdentity(join.Identity)),
EncryptionKey: rawKey, EncryptionKey: rawKey,
AcceptSignature: signature, AcceptSignature: signature,
UserInviteId: join.InviteId, InviteId: join.InviteId,
EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey), EncryptedReadKeys: t.encryptReadKeys(join.EncryptedReadKeys, encKey),
}, },
}, },
@ -203,9 +216,9 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACL
GetKey(invite.EncryptionKey).(encryptionkey.PrivKey) GetKey(invite.EncryptionKey).(encryptionkey.PrivKey)
rawEncKey, _ := encKey.GetPublic().Raw() rawEncKey, _ := encKey.GetPublic().Raw()
convCh = &aclpb.ACLChangeACLContentValue{ convCh = &aclrecordproto.ACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserInvite{ Value: &aclrecordproto.ACLContentValue_UserInvite{
UserInvite: &aclpb.ACLChangeUserInvite{ UserInvite: &aclrecordproto.ACLUserInvite{
AcceptPublicKey: rawAcceptKey, AcceptPublicKey: rawAcceptKey,
EncryptPublicKey: rawEncKey, EncryptPublicKey: rawEncKey,
EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey), EncryptedReadKeys: t.encryptReadKeys(invite.EncryptedReadKeys, encKey),
@ -214,24 +227,13 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACL
}, },
}, },
} }
case ch.UserConfirm != nil:
confirm := ch.UserConfirm
convCh = &aclpb.ACLChangeACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserConfirm{
UserConfirm: &aclpb.ACLChangeUserConfirm{
Identity: t.keychain.GetIdentity(confirm.Identity),
UserAddId: confirm.UserAddId,
},
},
}
case ch.UserPermissionChange != nil: case ch.UserPermissionChange != nil:
permissionChange := ch.UserPermissionChange permissionChange := ch.UserPermissionChange
convCh = &aclpb.ACLChangeACLContentValue{ convCh = &aclrecordproto.ACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserPermissionChange{ Value: &aclrecordproto.ACLContentValue_UserPermissionChange{
UserPermissionChange: &aclpb.ACLChangeUserPermissionChange{ UserPermissionChange: &aclrecordproto.ACLUserPermissionChange{
Identity: t.keychain.GetIdentity(permissionChange.Identity), Identity: []byte(t.keychain.GetIdentity(permissionChange.Identity)),
Permissions: t.convertPermission(permissionChange.Permission), Permissions: t.convertPermission(permissionChange.Permission),
}, },
}, },
@ -241,26 +243,25 @@ func (t *ACLListStorageBuilder) parseACLChange(ch *ACLChange) (convCh *aclpb.ACL
newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey) newReadKey := t.keychain.GetKey(remove.NewReadKey).(*SymKey)
var replaces []*aclpb.ACLChangeReadKeyReplace var replaces []*aclrecordproto.ACLReadKeyReplace
for _, id := range remove.IdentitiesLeft { for _, id := range remove.IdentitiesLeft {
identity := t.keychain.GetIdentity(id) encKey := t.keychain.EncryptionKeysByYAMLIdentity[id]
encKey := t.keychain.EncryptionKeys[id]
rawEncKey, _ := encKey.GetPublic().Raw() rawEncKey, _ := encKey.GetPublic().Raw()
encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes()) encReadKey, err := encKey.GetPublic().Encrypt(newReadKey.Key.Bytes())
if err != nil { if err != nil {
panic(err) panic(err)
} }
replaces = append(replaces, &aclpb.ACLChangeReadKeyReplace{ replaces = append(replaces, &aclrecordproto.ACLReadKeyReplace{
Identity: identity, Identity: []byte(t.keychain.GetIdentity(id)),
EncryptionKey: rawEncKey, EncryptionKey: rawEncKey,
EncryptedReadKey: encReadKey, EncryptedReadKey: encReadKey,
}) })
} }
convCh = &aclpb.ACLChangeACLContentValue{ convCh = &aclrecordproto.ACLContentValue{
Value: &aclpb.ACLChangeACLContentValueValueOfUserRemove{ Value: &aclrecordproto.ACLContentValue_UserRemove{
UserRemove: &aclpb.ACLChangeUserRemove{ UserRemove: &aclrecordproto.ACLUserRemove{
Identity: t.keychain.GetIdentity(remove.RemovedIdentity), Identity: []byte(t.keychain.GetIdentity(remove.RemovedIdentity)),
ReadKeyReplaces: replaces, ReadKeyReplaces: replaces,
}, },
}, },
@ -286,20 +287,20 @@ func (t *ACLListStorageBuilder) encryptReadKeys(keys []string, encKey encryption
return return
} }
func (t *ACLListStorageBuilder) convertPermission(perm string) aclpb.ACLChangeUserPermissions { func (t *ACLListStorageBuilder) convertPermission(perm string) aclrecordproto.ACLUserPermissions {
switch perm { switch perm {
case "admin": case "admin":
return aclpb.ACLChange_Admin return aclrecordproto.ACLUserPermissions_Admin
case "writer": case "writer":
return aclpb.ACLChange_Writer return aclrecordproto.ACLUserPermissions_Writer
case "reader": case "reader":
return aclpb.ACLChange_Reader return aclrecordproto.ACLUserPermissions_Reader
default: default:
panic(fmt.Sprintf("incorrect permission: %s", perm)) panic(fmt.Sprintf("incorrect permission: %s", perm))
} }
} }
func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclpb.Record, id string) error) (err error) { func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclrecordproto.ACLRecord, id string) error) (err error) {
for i := len(t.records) - 1; i >= 0; i-- { for i := len(t.records) - 1; i >= 0; i-- {
err = f(t.records[i], t.rawRecords[i].Id) err = f(t.records[i], t.rawRecords[i].Id)
if err != nil { if err != nil {
@ -309,14 +310,20 @@ func (t *ACLListStorageBuilder) traverseFromHead(f func(rec *aclpb.Record, id st
return nil return nil
} }
func (t *ACLListStorageBuilder) createHeaderAndId() { func (t *ACLListStorageBuilder) parseRoot(root *Root) {
t.header = &aclpb.Header{ rawSignKey, _ := t.keychain.SigningKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
FirstId: t.rawRecords[0].Id, rawEncKey, _ := t.keychain.EncryptionKeysByYAMLIdentity[root.Identity].GetPublic().Raw()
AclListId: "", readKey, _ := aclrecordproto.ACLReadKeyDerive(rawSignKey, rawEncKey)
WorkspaceId: "", hasher := fnv.New64()
DocType: aclpb.Header_ACL, hasher.Write(readKey.Bytes())
t.root = &aclrecordproto.ACLRoot{
Identity: rawSignKey,
EncryptionKey: rawEncKey,
SpaceId: root.SpaceId,
EncryptedReadKey: nil,
DerivationScheme: "scheme",
CurrentReadKeyHash: hasher.Sum64(),
} }
bytes, _ := t.header.Marshal() t.rawRoot = t.createRaw(t.root, rawSignKey)
id, _ := cid.NewCIDFromBytes(bytes) t.id = t.rawRoot.Id
t.id = id
} }

View File

@ -9,7 +9,7 @@ package acllistbuilder
import ( import (
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"strings" "strings"
@ -33,11 +33,11 @@ func (t *ACLListStorageBuilder) Graph() (string, error) {
graph.SetDir(true) graph.SetDir(true)
var nodes = make(map[string]struct{}) var nodes = make(map[string]struct{})
var addNodes = func(r *aclpb.Record, id string) error { var addNodes = func(r *aclrecordproto.ACLRecord, id string) error {
style := "solid" style := "solid"
var chSymbs []string var chSymbs []string
aclData := &aclpb.ACLChangeACLData{} aclData := &aclrecordproto.ACLData{}
err := proto.Unmarshal(r.GetData(), aclData) err := proto.Unmarshal(r.GetData(), aclData)
if err != nil { if err != nil {
return err return err
@ -92,7 +92,7 @@ func (t *ACLListStorageBuilder) Graph() (string, error) {
return nil return nil
} }
var addLinks = func(r *aclpb.Record, id string) error { var addLinks = func(r *aclrecordproto.ACLRecord, id string) error {
if r.PrevId == "" { if r.PrevId == "" {
return nil return nil
} }

View File

@ -6,9 +6,10 @@ type Key struct {
} }
type Keys struct { type Keys struct {
Enc []*Key `yaml:"Enc"` Derived string `yaml:"Derived"`
Sign []*Key `yaml:"Sign"` Enc []*Key `yaml:"Enc"`
Read []*Key `yaml:"Read"` Sign []*Key `yaml:"Sign"`
Read []*Key `yaml:"Read"`
} }
type ACLChange struct { type ACLChange struct {
@ -35,11 +36,6 @@ type ACLChange struct {
InviteId string `yaml:"inviteId"` InviteId string `yaml:"inviteId"`
} `yaml:"userInvite"` } `yaml:"userInvite"`
UserConfirm *struct {
Identity string `yaml:"identity"`
UserAddId string `yaml:"UserAddId"`
} `yaml:"userConfirm"`
UserRemove *struct { UserRemove *struct {
RemovedIdentity string `yaml:"removedIdentity"` RemovedIdentity string `yaml:"removedIdentity"`
NewReadKey string `yaml:"newReadKey"` NewReadKey string `yaml:"newReadKey"`
@ -63,7 +59,13 @@ type Header struct {
IsWorkspace bool `yaml:"isWorkspace"` IsWorkspace bool `yaml:"isWorkspace"`
} }
type Root struct {
Identity string `yaml:"identity"`
SpaceId string `yaml:"spaceId"`
}
type YMLList struct { type YMLList struct {
Root *Root
Records []*Record `yaml:"records"` Records []*Record `yaml:"records"`
Keys Keys `yaml:"keys"` Keys Keys `yaml:"keys"`

View File

@ -58,24 +58,24 @@ func (m *PlainTextChange) XXX_DiscardUnknown() {
var xxx_messageInfo_PlainTextChange proto.InternalMessageInfo var xxx_messageInfo_PlainTextChange proto.InternalMessageInfo
type PlainTextChangeContent struct { type PlainTextChange_Content struct {
// Types that are valid to be assigned to Value: // Types that are valid to be assigned to Value:
// *PlainTextChangeContentValueOfTextAppend // *PlainTextChange_Content_TextAppend
Value IsPlainTextChangeContentValue `protobuf_oneof:"value"` Value isPlainTextChange_Content_Value `protobuf_oneof:"value"`
} }
func (m *PlainTextChangeContent) Reset() { *m = PlainTextChangeContent{} } func (m *PlainTextChange_Content) Reset() { *m = PlainTextChange_Content{} }
func (m *PlainTextChangeContent) String() string { return proto.CompactTextString(m) } func (m *PlainTextChange_Content) String() string { return proto.CompactTextString(m) }
func (*PlainTextChangeContent) ProtoMessage() {} func (*PlainTextChange_Content) ProtoMessage() {}
func (*PlainTextChangeContent) Descriptor() ([]byte, []int) { func (*PlainTextChange_Content) Descriptor() ([]byte, []int) {
return fileDescriptor_37f33c266ada4318, []int{0, 0} return fileDescriptor_37f33c266ada4318, []int{0, 0}
} }
func (m *PlainTextChangeContent) XXX_Unmarshal(b []byte) error { func (m *PlainTextChange_Content) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
} }
func (m *PlainTextChangeContent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlainTextChange_Content) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic { if deterministic {
return xxx_messageInfo_PlainTextChangeContent.Marshal(b, m, deterministic) return xxx_messageInfo_PlainTextChange_Content.Marshal(b, m, deterministic)
} else { } else {
b = b[:cap(b)] b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b) n, err := m.MarshalToSizedBuffer(b)
@ -85,67 +85,67 @@ func (m *PlainTextChangeContent) XXX_Marshal(b []byte, deterministic bool) ([]by
return b[:n], nil return b[:n], nil
} }
} }
func (m *PlainTextChangeContent) XXX_Merge(src proto.Message) { func (m *PlainTextChange_Content) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlainTextChangeContent.Merge(m, src) xxx_messageInfo_PlainTextChange_Content.Merge(m, src)
} }
func (m *PlainTextChangeContent) XXX_Size() int { func (m *PlainTextChange_Content) XXX_Size() int {
return m.Size() return m.Size()
} }
func (m *PlainTextChangeContent) XXX_DiscardUnknown() { func (m *PlainTextChange_Content) XXX_DiscardUnknown() {
xxx_messageInfo_PlainTextChangeContent.DiscardUnknown(m) xxx_messageInfo_PlainTextChange_Content.DiscardUnknown(m)
} }
var xxx_messageInfo_PlainTextChangeContent proto.InternalMessageInfo var xxx_messageInfo_PlainTextChange_Content proto.InternalMessageInfo
type IsPlainTextChangeContentValue interface { type isPlainTextChange_Content_Value interface {
IsPlainTextChangeContentValue() isPlainTextChange_Content_Value()
MarshalTo([]byte) (int, error) MarshalTo([]byte) (int, error)
Size() int Size() int
} }
type PlainTextChangeContentValueOfTextAppend struct { type PlainTextChange_Content_TextAppend struct {
TextAppend *PlainTextChangeTextAppend `protobuf:"bytes,1,opt,name=textAppend,proto3,oneof" json:"textAppend,omitempty"` TextAppend *PlainTextChange_TextAppend `protobuf:"bytes,1,opt,name=textAppend,proto3,oneof" json:"textAppend,omitempty"`
} }
func (*PlainTextChangeContentValueOfTextAppend) IsPlainTextChangeContentValue() {} func (*PlainTextChange_Content_TextAppend) isPlainTextChange_Content_Value() {}
func (m *PlainTextChangeContent) GetValue() IsPlainTextChangeContentValue { func (m *PlainTextChange_Content) GetValue() isPlainTextChange_Content_Value {
if m != nil { if m != nil {
return m.Value return m.Value
} }
return nil return nil
} }
func (m *PlainTextChangeContent) GetTextAppend() *PlainTextChangeTextAppend { func (m *PlainTextChange_Content) GetTextAppend() *PlainTextChange_TextAppend {
if x, ok := m.GetValue().(*PlainTextChangeContentValueOfTextAppend); ok { if x, ok := m.GetValue().(*PlainTextChange_Content_TextAppend); ok {
return x.TextAppend return x.TextAppend
} }
return nil return nil
} }
// XXX_OneofWrappers is for the internal use of the proto package. // XXX_OneofWrappers is for the internal use of the proto package.
func (*PlainTextChangeContent) XXX_OneofWrappers() []interface{} { func (*PlainTextChange_Content) XXX_OneofWrappers() []interface{} {
return []interface{}{ return []interface{}{
(*PlainTextChangeContentValueOfTextAppend)(nil), (*PlainTextChange_Content_TextAppend)(nil),
} }
} }
type PlainTextChangeTextAppend struct { type PlainTextChange_TextAppend struct {
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
} }
func (m *PlainTextChangeTextAppend) Reset() { *m = PlainTextChangeTextAppend{} } func (m *PlainTextChange_TextAppend) Reset() { *m = PlainTextChange_TextAppend{} }
func (m *PlainTextChangeTextAppend) String() string { return proto.CompactTextString(m) } func (m *PlainTextChange_TextAppend) String() string { return proto.CompactTextString(m) }
func (*PlainTextChangeTextAppend) ProtoMessage() {} func (*PlainTextChange_TextAppend) ProtoMessage() {}
func (*PlainTextChangeTextAppend) Descriptor() ([]byte, []int) { func (*PlainTextChange_TextAppend) Descriptor() ([]byte, []int) {
return fileDescriptor_37f33c266ada4318, []int{0, 1} return fileDescriptor_37f33c266ada4318, []int{0, 1}
} }
func (m *PlainTextChangeTextAppend) XXX_Unmarshal(b []byte) error { func (m *PlainTextChange_TextAppend) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
} }
func (m *PlainTextChangeTextAppend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlainTextChange_TextAppend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic { if deterministic {
return xxx_messageInfo_PlainTextChangeTextAppend.Marshal(b, m, deterministic) return xxx_messageInfo_PlainTextChange_TextAppend.Marshal(b, m, deterministic)
} else { } else {
b = b[:cap(b)] b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b) n, err := m.MarshalToSizedBuffer(b)
@ -155,41 +155,41 @@ func (m *PlainTextChangeTextAppend) XXX_Marshal(b []byte, deterministic bool) ([
return b[:n], nil return b[:n], nil
} }
} }
func (m *PlainTextChangeTextAppend) XXX_Merge(src proto.Message) { func (m *PlainTextChange_TextAppend) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlainTextChangeTextAppend.Merge(m, src) xxx_messageInfo_PlainTextChange_TextAppend.Merge(m, src)
} }
func (m *PlainTextChangeTextAppend) XXX_Size() int { func (m *PlainTextChange_TextAppend) XXX_Size() int {
return m.Size() return m.Size()
} }
func (m *PlainTextChangeTextAppend) XXX_DiscardUnknown() { func (m *PlainTextChange_TextAppend) XXX_DiscardUnknown() {
xxx_messageInfo_PlainTextChangeTextAppend.DiscardUnknown(m) xxx_messageInfo_PlainTextChange_TextAppend.DiscardUnknown(m)
} }
var xxx_messageInfo_PlainTextChangeTextAppend proto.InternalMessageInfo var xxx_messageInfo_PlainTextChange_TextAppend proto.InternalMessageInfo
func (m *PlainTextChangeTextAppend) GetText() string { func (m *PlainTextChange_TextAppend) GetText() string {
if m != nil { if m != nil {
return m.Text return m.Text
} }
return "" return ""
} }
type PlainTextChangeSnapshot struct { type PlainTextChange_Snapshot struct {
Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"`
} }
func (m *PlainTextChangeSnapshot) Reset() { *m = PlainTextChangeSnapshot{} } func (m *PlainTextChange_Snapshot) Reset() { *m = PlainTextChange_Snapshot{} }
func (m *PlainTextChangeSnapshot) String() string { return proto.CompactTextString(m) } func (m *PlainTextChange_Snapshot) String() string { return proto.CompactTextString(m) }
func (*PlainTextChangeSnapshot) ProtoMessage() {} func (*PlainTextChange_Snapshot) ProtoMessage() {}
func (*PlainTextChangeSnapshot) Descriptor() ([]byte, []int) { func (*PlainTextChange_Snapshot) Descriptor() ([]byte, []int) {
return fileDescriptor_37f33c266ada4318, []int{0, 2} return fileDescriptor_37f33c266ada4318, []int{0, 2}
} }
func (m *PlainTextChangeSnapshot) XXX_Unmarshal(b []byte) error { func (m *PlainTextChange_Snapshot) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
} }
func (m *PlainTextChangeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlainTextChange_Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic { if deterministic {
return xxx_messageInfo_PlainTextChangeSnapshot.Marshal(b, m, deterministic) return xxx_messageInfo_PlainTextChange_Snapshot.Marshal(b, m, deterministic)
} else { } else {
b = b[:cap(b)] b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b) n, err := m.MarshalToSizedBuffer(b)
@ -199,42 +199,42 @@ func (m *PlainTextChangeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]b
return b[:n], nil return b[:n], nil
} }
} }
func (m *PlainTextChangeSnapshot) XXX_Merge(src proto.Message) { func (m *PlainTextChange_Snapshot) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlainTextChangeSnapshot.Merge(m, src) xxx_messageInfo_PlainTextChange_Snapshot.Merge(m, src)
} }
func (m *PlainTextChangeSnapshot) XXX_Size() int { func (m *PlainTextChange_Snapshot) XXX_Size() int {
return m.Size() return m.Size()
} }
func (m *PlainTextChangeSnapshot) XXX_DiscardUnknown() { func (m *PlainTextChange_Snapshot) XXX_DiscardUnknown() {
xxx_messageInfo_PlainTextChangeSnapshot.DiscardUnknown(m) xxx_messageInfo_PlainTextChange_Snapshot.DiscardUnknown(m)
} }
var xxx_messageInfo_PlainTextChangeSnapshot proto.InternalMessageInfo var xxx_messageInfo_PlainTextChange_Snapshot proto.InternalMessageInfo
func (m *PlainTextChangeSnapshot) GetText() string { func (m *PlainTextChange_Snapshot) GetText() string {
if m != nil { if m != nil {
return m.Text return m.Text
} }
return "" return ""
} }
type PlainTextChangeData struct { type PlainTextChange_Data struct {
Content []*PlainTextChangeContent `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"` Content []*PlainTextChange_Content `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"`
Snapshot *PlainTextChangeSnapshot `protobuf:"bytes,2,opt,name=snapshot,proto3" json:"snapshot,omitempty"` Snapshot *PlainTextChange_Snapshot `protobuf:"bytes,2,opt,name=snapshot,proto3" json:"snapshot,omitempty"`
} }
func (m *PlainTextChangeData) Reset() { *m = PlainTextChangeData{} } func (m *PlainTextChange_Data) Reset() { *m = PlainTextChange_Data{} }
func (m *PlainTextChangeData) String() string { return proto.CompactTextString(m) } func (m *PlainTextChange_Data) String() string { return proto.CompactTextString(m) }
func (*PlainTextChangeData) ProtoMessage() {} func (*PlainTextChange_Data) ProtoMessage() {}
func (*PlainTextChangeData) Descriptor() ([]byte, []int) { func (*PlainTextChange_Data) Descriptor() ([]byte, []int) {
return fileDescriptor_37f33c266ada4318, []int{0, 3} return fileDescriptor_37f33c266ada4318, []int{0, 3}
} }
func (m *PlainTextChangeData) XXX_Unmarshal(b []byte) error { func (m *PlainTextChange_Data) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b) return m.Unmarshal(b)
} }
func (m *PlainTextChangeData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { func (m *PlainTextChange_Data) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic { if deterministic {
return xxx_messageInfo_PlainTextChangeData.Marshal(b, m, deterministic) return xxx_messageInfo_PlainTextChange_Data.Marshal(b, m, deterministic)
} else { } else {
b = b[:cap(b)] b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b) n, err := m.MarshalToSizedBuffer(b)
@ -244,26 +244,26 @@ func (m *PlainTextChangeData) XXX_Marshal(b []byte, deterministic bool) ([]byte,
return b[:n], nil return b[:n], nil
} }
} }
func (m *PlainTextChangeData) XXX_Merge(src proto.Message) { func (m *PlainTextChange_Data) XXX_Merge(src proto.Message) {
xxx_messageInfo_PlainTextChangeData.Merge(m, src) xxx_messageInfo_PlainTextChange_Data.Merge(m, src)
} }
func (m *PlainTextChangeData) XXX_Size() int { func (m *PlainTextChange_Data) XXX_Size() int {
return m.Size() return m.Size()
} }
func (m *PlainTextChangeData) XXX_DiscardUnknown() { func (m *PlainTextChange_Data) XXX_DiscardUnknown() {
xxx_messageInfo_PlainTextChangeData.DiscardUnknown(m) xxx_messageInfo_PlainTextChange_Data.DiscardUnknown(m)
} }
var xxx_messageInfo_PlainTextChangeData proto.InternalMessageInfo var xxx_messageInfo_PlainTextChange_Data proto.InternalMessageInfo
func (m *PlainTextChangeData) GetContent() []*PlainTextChangeContent { func (m *PlainTextChange_Data) GetContent() []*PlainTextChange_Content {
if m != nil { if m != nil {
return m.Content return m.Content
} }
return nil return nil
} }
func (m *PlainTextChangeData) GetSnapshot() *PlainTextChangeSnapshot { func (m *PlainTextChange_Data) GetSnapshot() *PlainTextChange_Snapshot {
if m != nil { if m != nil {
return m.Snapshot return m.Snapshot
} }
@ -272,10 +272,10 @@ func (m *PlainTextChangeData) GetSnapshot() *PlainTextChangeSnapshot {
func init() { func init() {
proto.RegisterType((*PlainTextChange)(nil), "anytype.PlainTextChange") proto.RegisterType((*PlainTextChange)(nil), "anytype.PlainTextChange")
proto.RegisterType((*PlainTextChangeContent)(nil), "anytype.PlainTextChange.Content") proto.RegisterType((*PlainTextChange_Content)(nil), "anytype.PlainTextChange.Content")
proto.RegisterType((*PlainTextChangeTextAppend)(nil), "anytype.PlainTextChange.TextAppend") proto.RegisterType((*PlainTextChange_TextAppend)(nil), "anytype.PlainTextChange.TextAppend")
proto.RegisterType((*PlainTextChangeSnapshot)(nil), "anytype.PlainTextChange.Snapshot") proto.RegisterType((*PlainTextChange_Snapshot)(nil), "anytype.PlainTextChange.Snapshot")
proto.RegisterType((*PlainTextChangeData)(nil), "anytype.PlainTextChange.Data") proto.RegisterType((*PlainTextChange_Data)(nil), "anytype.PlainTextChange.Data")
} }
func init() { func init() {
@ -326,7 +326,7 @@ func (m *PlainTextChange) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *PlainTextChangeContent) Marshal() (dAtA []byte, err error) { func (m *PlainTextChange_Content) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size]) n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -336,12 +336,12 @@ func (m *PlainTextChangeContent) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil return dAtA[:n], nil
} }
func (m *PlainTextChangeContent) MarshalTo(dAtA []byte) (int, error) { func (m *PlainTextChange_Content) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *PlainTextChangeContent) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *PlainTextChange_Content) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
_ = i _ = i
var l int var l int
@ -358,12 +358,12 @@ func (m *PlainTextChangeContent) MarshalToSizedBuffer(dAtA []byte) (int, error)
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *PlainTextChangeContentValueOfTextAppend) MarshalTo(dAtA []byte) (int, error) { func (m *PlainTextChange_Content_TextAppend) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *PlainTextChangeContentValueOfTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *PlainTextChange_Content_TextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
if m.TextAppend != nil { if m.TextAppend != nil {
{ {
@ -379,7 +379,7 @@ func (m *PlainTextChangeContentValueOfTextAppend) MarshalToSizedBuffer(dAtA []by
} }
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *PlainTextChangeTextAppend) Marshal() (dAtA []byte, err error) { func (m *PlainTextChange_TextAppend) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size]) n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -389,12 +389,12 @@ func (m *PlainTextChangeTextAppend) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil return dAtA[:n], nil
} }
func (m *PlainTextChangeTextAppend) MarshalTo(dAtA []byte) (int, error) { func (m *PlainTextChange_TextAppend) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *PlainTextChangeTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *PlainTextChange_TextAppend) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
_ = i _ = i
var l int var l int
@ -409,7 +409,7 @@ func (m *PlainTextChangeTextAppend) MarshalToSizedBuffer(dAtA []byte) (int, erro
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *PlainTextChangeSnapshot) Marshal() (dAtA []byte, err error) { func (m *PlainTextChange_Snapshot) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size]) n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -419,12 +419,12 @@ func (m *PlainTextChangeSnapshot) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil return dAtA[:n], nil
} }
func (m *PlainTextChangeSnapshot) MarshalTo(dAtA []byte) (int, error) { func (m *PlainTextChange_Snapshot) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *PlainTextChangeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *PlainTextChange_Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
_ = i _ = i
var l int var l int
@ -439,7 +439,7 @@ func (m *PlainTextChangeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error)
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *PlainTextChangeData) Marshal() (dAtA []byte, err error) { func (m *PlainTextChange_Data) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
dAtA = make([]byte, size) dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size]) n, err := m.MarshalToSizedBuffer(dAtA[:size])
@ -449,12 +449,12 @@ func (m *PlainTextChangeData) Marshal() (dAtA []byte, err error) {
return dAtA[:n], nil return dAtA[:n], nil
} }
func (m *PlainTextChangeData) MarshalTo(dAtA []byte) (int, error) { func (m *PlainTextChange_Data) MarshalTo(dAtA []byte) (int, error) {
size := m.Size() size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size]) return m.MarshalToSizedBuffer(dAtA[:size])
} }
func (m *PlainTextChangeData) MarshalToSizedBuffer(dAtA []byte) (int, error) { func (m *PlainTextChange_Data) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA) i := len(dAtA)
_ = i _ = i
var l int var l int
@ -508,7 +508,7 @@ func (m *PlainTextChange) Size() (n int) {
return n return n
} }
func (m *PlainTextChangeContent) Size() (n int) { func (m *PlainTextChange_Content) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -520,7 +520,7 @@ func (m *PlainTextChangeContent) Size() (n int) {
return n return n
} }
func (m *PlainTextChangeContentValueOfTextAppend) Size() (n int) { func (m *PlainTextChange_Content_TextAppend) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -532,7 +532,7 @@ func (m *PlainTextChangeContentValueOfTextAppend) Size() (n int) {
} }
return n return n
} }
func (m *PlainTextChangeTextAppend) Size() (n int) { func (m *PlainTextChange_TextAppend) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -545,7 +545,7 @@ func (m *PlainTextChangeTextAppend) Size() (n int) {
return n return n
} }
func (m *PlainTextChangeSnapshot) Size() (n int) { func (m *PlainTextChange_Snapshot) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -558,7 +558,7 @@ func (m *PlainTextChangeSnapshot) Size() (n int) {
return n return n
} }
func (m *PlainTextChangeData) Size() (n int) { func (m *PlainTextChange_Data) Size() (n int) {
if m == nil { if m == nil {
return 0 return 0
} }
@ -633,7 +633,7 @@ func (m *PlainTextChange) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error { func (m *PlainTextChange_Content) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {
@ -691,11 +691,11 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
v := &PlainTextChangeTextAppend{} v := &PlainTextChange_TextAppend{}
if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err return err
} }
m.Value = &PlainTextChangeContentValueOfTextAppend{v} m.Value = &PlainTextChange_Content_TextAppend{v}
iNdEx = postIndex iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
@ -718,7 +718,7 @@ func (m *PlainTextChangeContent) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error { func (m *PlainTextChange_TextAppend) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {
@ -800,7 +800,7 @@ func (m *PlainTextChangeTextAppend) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error { func (m *PlainTextChange_Snapshot) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {
@ -882,7 +882,7 @@ func (m *PlainTextChangeSnapshot) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error { func (m *PlainTextChange_Data) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {
@ -940,7 +940,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
m.Content = append(m.Content, &PlainTextChangeContent{}) m.Content = append(m.Content, &PlainTextChange_Content{})
if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err return err
} }
@ -975,7 +975,7 @@ func (m *PlainTextChangeData) Unmarshal(dAtA []byte) error {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
if m.Snapshot == nil { if m.Snapshot == nil {
m.Snapshot = &PlainTextChangeSnapshot{} m.Snapshot = &PlainTextChange_Snapshot{}
} }
if err := m.Snapshot.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { if err := m.Snapshot.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err return err

View File

@ -1,12 +1,7 @@
root:
identity: A
spaceId: space
records: records:
- identity: A
aclChanges:
- userAdd:
identity: A
permission: admin
encryptionKey: key.Enc.A
encryptedReadKeys: [key.Read.1]
readKey: key.Read.1
- identity: A - identity: A
aclChanges: aclChanges:
- userInvite: - userInvite:
@ -31,6 +26,7 @@ records:
encryptedReadKeys: [key.Read.1] encryptedReadKeys: [key.Read.1]
readKey: key.Read.1 readKey: key.Read.1
keys: keys:
Derived: A
Enc: Enc:
- name: A - name: A
value: generated value: generated
@ -51,4 +47,4 @@ keys:
value: generated value: generated
Read: Read:
- name: 1 - name: 1
value: generated value: derived

View File

@ -1,12 +1,7 @@
root:
identity: A
spaceId: space
records: records:
- identity: A
aclChanges:
- userAdd:
identity: A
permission: admin
encryptionKey: key.Enc.A
encryptedReadKeys: [key.Read.1]
readKey: key.Read.1
- identity: A - identity: A
aclChanges: aclChanges:
- userInvite: - userInvite:
@ -38,6 +33,7 @@ records:
identitiesLeft: [A, C] identitiesLeft: [A, C]
readKey: key.Read.2 readKey: key.Read.2
keys: keys:
Derived: A
Enc: Enc:
- name: A - name: A
value: generated value: generated
@ -58,6 +54,6 @@ keys:
value: generated value: generated
Read: Read:
- name: 1 - name: 1
value: generated value: derived
- name: 2 - name: 2
value: generated value: generated

View File

@ -2,11 +2,7 @@ package tree
import ( import (
"errors" "errors"
"fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/gogo/protobuf/proto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
) )
var ( var (
@ -14,66 +10,52 @@ var (
ErrIncorrectCID = errors.New("change has incorrect CID") ErrIncorrectCID = errors.New("change has incorrect CID")
) )
type ChangeContent struct {
ChangesData proto.Marshaler
ACLData *aclpb.ACLChangeACLData
Id string // TODO: this is just for testing, because id should be created automatically from content
}
// Change is an abstract type for all types of changes // Change is an abstract type for all types of changes
type Change struct { type Change struct {
Next []*Change Next []*Change
PreviousIds []string PreviousIds []string
Id string AclHeadId string
SnapshotId string Id string
IsSnapshot bool SnapshotId string
DecryptedChange []byte // TODO: check if we need it IsSnapshot bool
ParsedModel interface{} // TODO: check if we need it Timestamp int64
ReadKeyHash uint64
Identity string
Data []byte
Model interface{}
// iterator helpers // iterator helpers
visited bool visited bool
branchesFinished bool branchesFinished bool
Content *aclpb.Change Signature []byte
Sign []byte
} }
func (ch *Change) ProtoChange() proto.Marshaler { func NewChange(id string, ch *treechangeproto.TreeChange, signature []byte) *Change {
return ch.Content
}
func (ch *Change) DecryptContents(key *symmetric.Key) error {
// if the document is already decrypted
if ch.Content.CurrentReadKeyHash == 0 {
return nil
}
decrypted, err := key.Decrypt(ch.Content.ChangesData)
if err != nil {
return fmt.Errorf("failed to decrypt changes data: %w", err)
}
ch.DecryptedChange = decrypted
return nil
}
func NewChange(id string, ch *aclpb.Change, signature []byte) *Change {
return &Change{ return &Change{
Next: nil, Next: nil,
PreviousIds: ch.TreeHeadIds, PreviousIds: ch.TreeHeadIds,
AclHeadId: ch.AclHeadId,
Timestamp: ch.Timestamp,
ReadKeyHash: ch.CurrentReadKeyHash,
Id: id, Id: id,
Content: ch, Data: ch.ChangesData,
SnapshotId: ch.SnapshotBaseId, SnapshotId: ch.SnapshotBaseId,
IsSnapshot: ch.IsSnapshot, IsSnapshot: ch.IsSnapshot,
Sign: signature, Identity: string(ch.Identity),
Signature: signature,
} }
} }
func (ch *Change) DecryptedChangeContent() []byte { func NewChangeFromRoot(id string, ch *treechangeproto.RootChange, signature []byte) *Change {
return ch.DecryptedChange return &Change{
} Next: nil,
AclHeadId: ch.AclHeadId,
func (ch *Change) Signature() []byte { Id: id,
return ch.Sign IsSnapshot: true,
Identity: string(ch.Identity),
Signature: signature,
}
} }
func (ch *Change) CID() string { func (ch *Change) CID() string {

View File

@ -1,8 +1,9 @@
package tree package tree
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
@ -10,134 +11,241 @@ import (
"time" "time"
) )
const componentBuilder = "tree.changebuilder" var ErrEmptyChange = errors.New("change payload should not be empty")
type BuilderContent struct { type BuilderContent struct {
treeHeadIds []string TreeHeadIds []string
aclHeadId string AclHeadId string
snapshotBaseId string SnapshotBaseId string
currentReadKeyHash uint64 CurrentReadKeyHash uint64
identity string Identity []byte
isSnapshot bool IsSnapshot bool
signingKey signingkey.PrivKey SigningKey signingkey.PrivKey
readKey *symmetric.Key ReadKey *symmetric.Key
content proto.Marshaler Content []byte
}
type InitialContent struct {
AclHeadId string
Identity []byte
SigningKey signingkey.PrivKey
SpaceId string
Seed []byte
ChangeType string
Timestamp int64
} }
type ChangeBuilder interface { type ChangeBuilder interface {
ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) ConvertFromRaw(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error)
ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) BuildContent(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error)
BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) BuildInitialContent(payload InitialContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error)
BuildRaw(ch *Change) (*aclpb.RawChange, error) BuildRaw(ch *Change) (*treechangeproto.RawTreeChangeWithId, error)
SetRootRawChange(rawIdChange *treechangeproto.RawTreeChangeWithId)
} }
type changeBuilder struct { type changeBuilder struct {
keys *common.Keychain rootChange *treechangeproto.RawTreeChangeWithId
keys *common.Keychain
} }
func newChangeBuilder(keys *common.Keychain) ChangeBuilder { func newChangeBuilder(keys *common.Keychain, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
return &changeBuilder{keys: keys} return &changeBuilder{keys: keys, rootChange: rootChange}
} }
func (c *changeBuilder) ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) { func (c *changeBuilder) ConvertFromRaw(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error) {
unmarshalled := &aclpb.Change{} if rawIdChange.GetRawChange() == nil {
err = proto.Unmarshal(rawChange.Payload, unmarshalled) err = ErrEmptyChange
if err != nil { return
return nil, err
} }
ch = NewChange(rawChange.Id, unmarshalled, rawChange.Signature) if verify {
return // verifying ID
if !cid.VerifyCID(rawIdChange.RawChange, rawIdChange.Id) {
err = ErrIncorrectCID
return
}
}
raw := &treechangeproto.RawTreeChange{} // TODO: sync pool
err = proto.Unmarshal(rawIdChange.GetRawChange(), raw)
if err != nil {
return
}
if verify {
var identityKey signingkey.PubKey
identityKey, err = c.keys.GetOrAdd(ch.Identity)
if err != nil {
return
}
// verifying signature
var res bool
res, err = identityKey.Verify(raw.Payload, raw.Signature)
if err != nil {
return
}
if !res {
err = ErrIncorrectSignature
return
}
}
return c.unmarshallRawChange(raw, rawIdChange.Id)
} }
func (c *changeBuilder) ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) { func (c *changeBuilder) SetRootRawChange(rawIdChange *treechangeproto.RawTreeChangeWithId) {
unmarshalled := &aclpb.Change{} c.rootChange = rawIdChange
ch, err = c.ConvertFromRaw(rawChange)
if err != nil {
return nil, err
}
identityKey, err := c.keys.GetOrAdd(unmarshalled.Identity)
if err != nil {
return
}
// verifying signature
res, err := identityKey.Verify(rawChange.Payload, rawChange.Signature)
if err != nil {
return
}
if !res {
err = ErrIncorrectSignature
return
}
// verifying ID
if !cid.VerifyCID(rawChange.Payload, rawChange.Id) {
err = ErrIncorrectCID
}
return
} }
func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) { func (c *changeBuilder) BuildInitialContent(payload InitialContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
aclChange := &aclpb.Change{ change := &treechangeproto.RootChange{
TreeHeadIds: payload.treeHeadIds, AclHeadId: payload.AclHeadId,
AclHeadId: payload.aclHeadId, Timestamp: payload.Timestamp,
SnapshotBaseId: payload.snapshotBaseId, Identity: payload.Identity,
CurrentReadKeyHash: payload.currentReadKeyHash, ChangeType: payload.ChangeType,
Timestamp: int64(time.Now().Nanosecond()), SpaceId: payload.SpaceId,
Identity: payload.identity, Seed: payload.Seed,
IsSnapshot: payload.isSnapshot,
} }
marshalledData, err := payload.content.Marshal()
marshalledChange, err := proto.Marshal(change)
if err != nil { if err != nil {
return return
} }
encrypted, err := payload.readKey.Encrypt(marshalledData) signature, err := payload.SigningKey.Sign(marshalledChange)
if err != nil {
return
}
aclChange.ChangesData = encrypted
fullMarshalledChange, err := proto.Marshal(aclChange)
if err != nil { if err != nil {
return return
} }
signature, err := payload.signingKey.Sign(fullMarshalledChange) raw := &treechangeproto.RawTreeChange{
if err != nil { Payload: marshalledChange,
return
}
id, err := cid.NewCIDFromBytes(fullMarshalledChange)
if err != nil {
return
}
ch = NewChange(id, aclChange, signature)
ch.ParsedModel = payload.content
raw = &aclpb.RawChange{
Payload: fullMarshalledChange,
Signature: signature, Signature: signature,
}
marshalledRawChange, err := proto.Marshal(raw)
if err != nil {
return
}
id, err := cid.NewCIDFromBytes(marshalledRawChange)
if err != nil {
return
}
ch = NewChangeFromRoot(id, change, signature)
rawIdChange = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: id, Id: id,
} }
return return
} }
func (c *changeBuilder) BuildRaw(ch *Change) (raw *aclpb.RawChange, err error) { func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
var marshalled []byte change := &treechangeproto.TreeChange{
marshalled, err = ch.Content.Marshal() TreeHeadIds: payload.TreeHeadIds,
AclHeadId: payload.AclHeadId,
SnapshotBaseId: payload.SnapshotBaseId,
CurrentReadKeyHash: payload.CurrentReadKeyHash,
Timestamp: int64(time.Now().Nanosecond()),
Identity: payload.Identity,
IsSnapshot: payload.IsSnapshot,
}
encrypted, err := payload.ReadKey.Encrypt(payload.Content)
if err != nil {
return
}
change.ChangesData = encrypted
marshalledChange, err := proto.Marshal(change)
if err != nil { if err != nil {
return return
} }
raw = &aclpb.RawChange{ signature, err := payload.SigningKey.Sign(marshalledChange)
if err != nil {
return
}
raw := &treechangeproto.RawTreeChange{
Payload: marshalledChange,
Signature: signature,
}
marshalledRawChange, err := proto.Marshal(raw)
if err != nil {
return
}
id, err := cid.NewCIDFromBytes(marshalledRawChange)
if err != nil {
return
}
ch = NewChange(id, change, signature)
ch.Model = payload.Content
rawIdChange = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: id,
}
return
}
func (c *changeBuilder) BuildRaw(ch *Change) (raw *treechangeproto.RawTreeChangeWithId, err error) {
if ch.Id == c.rootChange.Id {
return c.rootChange, nil
}
treeChange := &treechangeproto.TreeChange{
TreeHeadIds: ch.PreviousIds,
AclHeadId: ch.AclHeadId,
SnapshotBaseId: ch.SnapshotId,
ChangesData: ch.Data,
CurrentReadKeyHash: ch.ReadKeyHash,
Timestamp: ch.Timestamp,
Identity: []byte(ch.Identity),
IsSnapshot: ch.IsSnapshot,
}
var marshalled []byte
marshalled, err = treeChange.Marshal()
if err != nil {
return
}
marshalledRawChange, err := proto.Marshal(&treechangeproto.RawTreeChange{
Payload: marshalled, Payload: marshalled,
Signature: ch.Signature(), Signature: ch.Signature,
})
if err != nil {
return
}
raw = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: ch.Id, Id: ch.Id,
} }
return return
} }
func (c *changeBuilder) unmarshallRawChange(raw *treechangeproto.RawTreeChange, id string) (ch *Change, err error) {
if c.rootChange.Id == id {
unmarshalled := &treechangeproto.RootChange{}
err = proto.Unmarshal(raw.Payload, unmarshalled)
if err != nil {
return
}
ch = NewChangeFromRoot(id, unmarshalled, raw.Signature)
return
}
unmarshalled := &treechangeproto.TreeChange{}
err = proto.Unmarshal(raw.Payload, unmarshalled)
if err != nil {
return
}
ch = NewChange(id, unmarshalled, raw.Signature)
return
}

View File

@ -2,7 +2,7 @@ package tree
import ( import (
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclrecordproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
) )
@ -43,12 +43,12 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.ACLList, c
state = aclList.ACLState() state = aclList.ACLState()
) )
// checking if the user could write // checking if the user could write
perm, err = state.PermissionsAtRecord(c.Content.AclHeadId, c.Content.Identity) perm, err = state.PermissionsAtRecord(c.AclHeadId, c.Identity)
if err != nil { if err != nil {
return return
} }
if perm.Permission != aclpb.ACLChange_Writer && perm.Permission != aclpb.ACLChange_Admin { if perm.Permission != aclrecordproto.ACLUserPermissions_Writer && perm.Permission != aclrecordproto.ACLUserPermissions_Admin {
err = list.ErrInsufficientPermissions err = list.ErrInsufficientPermissions
return return
} }
@ -56,16 +56,16 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.ACLList, c
// checking if the change refers to later acl heads than its previous ids // checking if the change refers to later acl heads than its previous ids
for _, id := range c.PreviousIds { for _, id := range c.PreviousIds {
prevChange := tree.attached[id] prevChange := tree.attached[id]
if prevChange.Content.AclHeadId == c.Content.AclHeadId { if prevChange.AclHeadId == c.AclHeadId {
continue continue
} }
var after bool var after bool
after, err = aclList.IsAfter(c.Content.AclHeadId, prevChange.Content.AclHeadId) after, err = aclList.IsAfter(c.AclHeadId, prevChange.AclHeadId)
if err != nil { if err != nil {
return return
} }
if !after { if !after {
err = fmt.Errorf("current acl head id (%s) should be after each of the previous ones (%s)", c.Content.AclHeadId, prevChange.Content.AclHeadId) err = fmt.Errorf("current acl head id (%s) should be after each of the previous ones (%s)", c.AclHeadId, prevChange.AclHeadId)
return return
} }
} }

View File

@ -3,21 +3,14 @@ package tree
import ( import (
"context" "context"
"errors" "errors"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/common"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
"go.uber.org/zap"
"sync" "sync"
) )
type ObjectTreeUpdateListener interface {
Update(tree ObjectTree)
Rebuild(tree ObjectTree)
}
type RWLocker interface { type RWLocker interface {
sync.Locker sync.Locker
RLock() RLock()
@ -34,7 +27,7 @@ type AddResultSummary int
type AddResult struct { type AddResult struct {
OldHeads []string OldHeads []string
Heads []string Heads []string
Added []*aclpb.RawChange Added []*treechangeproto.RawTreeChangeWithId
Mode Mode Mode Mode
} }
@ -46,22 +39,22 @@ type ObjectTree interface {
RWLocker RWLocker
ID() string ID() string
Header() *aclpb.Header Header() *treechangeproto.RawTreeChangeWithId
Heads() []string Heads() []string
Root() *Change Root() *Change
HasChange(string) bool HasChange(string) bool
DebugDump() (string, error)
Iterate(convert ChangeConvertFunc, iterate ChangeIterateFunc) error Iterate(convert ChangeConvertFunc, iterate ChangeIterateFunc) error
IterateFrom(id string, convert ChangeConvertFunc, iterate ChangeIterateFunc) error IterateFrom(id string, convert ChangeConvertFunc, iterate ChangeIterateFunc) error
SnapshotPath() []string SnapshotPath() []string
ChangesAfterCommonSnapshot(snapshotPath, heads []string) ([]*aclpb.RawChange, error) ChangesAfterCommonSnapshot(snapshotPath, heads []string) ([]*treechangeproto.RawTreeChangeWithId, error)
Storage() storage.TreeStorage Storage() storage.TreeStorage
DebugDump() (string, error)
AddContent(ctx context.Context, content SignableChangeContent) (*aclpb.RawChange, error) AddContent(ctx context.Context, content SignableChangeContent) (AddResult, error)
AddRawChanges(ctx context.Context, changes ...*aclpb.RawChange) (AddResult, error) AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (AddResult, error)
Close() error Close() error
} }
@ -69,20 +62,19 @@ type ObjectTree interface {
type objectTree struct { type objectTree struct {
treeStorage storage.TreeStorage treeStorage storage.TreeStorage
changeBuilder ChangeBuilder changeBuilder ChangeBuilder
updateListener ObjectTreeUpdateListener
validator ObjectTreeValidator validator ObjectTreeValidator
rawChangeLoader *rawChangeLoader rawChangeLoader *rawChangeLoader
treeBuilder *treeBuilder treeBuilder *treeBuilder
aclList list.ACLList aclList list.ACLList
id string id string
header *aclpb.Header root *treechangeproto.RawTreeChangeWithId
tree *Tree tree *Tree
keys map[uint64]*symmetric.Key keys map[uint64]*symmetric.Key
// buffers // buffers
difSnapshotBuf []*aclpb.RawChange difSnapshotBuf []*treechangeproto.RawTreeChangeWithId
tmpChangesBuf []*Change tmpChangesBuf []*Change
newSnapshotsBuf []*Change newSnapshotsBuf []*Change
notSeenIdxBuf []int notSeenIdxBuf []int
@ -96,90 +88,29 @@ type objectTreeDeps struct {
changeBuilder ChangeBuilder changeBuilder ChangeBuilder
treeBuilder *treeBuilder treeBuilder *treeBuilder
treeStorage storage.TreeStorage treeStorage storage.TreeStorage
updateListener ObjectTreeUpdateListener
validator ObjectTreeValidator validator ObjectTreeValidator
rawChangeLoader *rawChangeLoader rawChangeLoader *rawChangeLoader
aclList list.ACLList aclList list.ACLList
} }
func defaultObjectTreeDeps( func defaultObjectTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage storage.TreeStorage, treeStorage storage.TreeStorage,
listener ObjectTreeUpdateListener,
aclList list.ACLList) objectTreeDeps { aclList list.ACLList) objectTreeDeps {
keychain := common.NewKeychain() keychain := common.NewKeychain()
changeBuilder := newChangeBuilder(keychain) changeBuilder := newChangeBuilder(keychain, rootChange)
treeBuilder := newTreeBuilder(treeStorage, changeBuilder) treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
return objectTreeDeps{ return objectTreeDeps{
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
treeBuilder: treeBuilder, treeBuilder: treeBuilder,
treeStorage: treeStorage, treeStorage: treeStorage,
updateListener: listener,
validator: newTreeValidator(), validator: newTreeValidator(),
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder), rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
aclList: aclList, aclList: aclList,
} }
} }
func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
objTree := &objectTree{
treeStorage: deps.treeStorage,
updateListener: deps.updateListener,
treeBuilder: deps.treeBuilder,
validator: deps.validator,
aclList: deps.aclList,
changeBuilder: deps.changeBuilder,
rawChangeLoader: deps.rawChangeLoader,
tree: nil,
keys: make(map[uint64]*symmetric.Key),
tmpChangesBuf: make([]*Change, 0, 10),
difSnapshotBuf: make([]*aclpb.RawChange, 0, 10),
notSeenIdxBuf: make([]int, 0, 10),
newSnapshotsBuf: make([]*Change, 0, 10),
}
err := objTree.rebuildFromStorage(nil)
if err != nil {
return nil, err
}
storageHeads, err := objTree.treeStorage.Heads()
if err != nil {
return nil, err
}
// comparing rebuilt heads with heads in storage
// in theory it can happen that we didn't set heads because the process has crashed
// therefore we want to set them later
if !slice.UnsortedEquals(storageHeads, objTree.tree.Heads()) {
log.With(zap.Strings("storage", storageHeads), zap.Strings("rebuilt", objTree.tree.Heads())).
Errorf("the heads in storage and objTree are different")
err = objTree.treeStorage.SetHeads(objTree.tree.Heads())
if err != nil {
return nil, err
}
}
objTree.id, err = objTree.treeStorage.ID()
if err != nil {
return nil, err
}
objTree.header, err = objTree.treeStorage.Header()
if err != nil {
return nil, err
}
if objTree.updateListener != nil {
objTree.updateListener.Rebuild(objTree)
}
return objTree, nil
}
func BuildObjectTree(treeStorage storage.TreeStorage, listener ObjectTreeUpdateListener, aclList list.ACLList) (ObjectTree, error) {
deps := defaultObjectTreeDeps(treeStorage, listener, aclList)
return buildObjectTree(deps)
}
func (ot *objectTree) rebuildFromStorage(newChanges []*Change) (err error) { func (ot *objectTree) rebuildFromStorage(newChanges []*Change) (err error) {
ot.treeBuilder.Reset() ot.treeBuilder.Reset()
@ -201,29 +132,27 @@ func (ot *objectTree) ID() string {
return ot.id return ot.id
} }
func (ot *objectTree) Header() *aclpb.Header { func (ot *objectTree) Header() *treechangeproto.RawTreeChangeWithId {
return ot.header return ot.root
} }
func (ot *objectTree) Storage() storage.TreeStorage { func (ot *objectTree) Storage() storage.TreeStorage {
return ot.treeStorage return ot.treeStorage
} }
func (ot *objectTree) AddContent(ctx context.Context, content SignableChangeContent) (rawChange *aclpb.RawChange, err error) { func (ot *objectTree) AddContent(ctx context.Context, content SignableChangeContent) (res AddResult, err error) {
defer func() {
if err == nil && ot.updateListener != nil {
ot.updateListener.Update(ot)
}
}()
payload, err := ot.prepareBuilderContent(content) payload, err := ot.prepareBuilderContent(content)
if err != nil { if err != nil {
return return
} }
// saving old heads
oldHeads := make([]string, 0, len(ot.tree.Heads()))
oldHeads = append(oldHeads, ot.tree.Heads()...)
objChange, rawChange, err := ot.changeBuilder.BuildContent(payload) objChange, rawChange, err := ot.changeBuilder.BuildContent(payload)
if content.IsSnapshot { if content.IsSnapshot {
// clearing tree, because we already fixed everything in the last snapshot // clearing tree, because we already saved everything in the last snapshot
ot.tree = &Tree{} ot.tree = &Tree{}
} }
err = ot.tree.AddMergedHead(objChange) err = ot.tree.AddMergedHead(objChange)
@ -237,6 +166,16 @@ func (ot *objectTree) AddContent(ctx context.Context, content SignableChangeCont
} }
err = ot.treeStorage.SetHeads([]string{objChange.Id}) err = ot.treeStorage.SetHeads([]string{objChange.Id})
if err != nil {
return
}
res = AddResult{
OldHeads: oldHeads,
Heads: []string{objChange.Id},
Added: []*treechangeproto.RawTreeChangeWithId{rawChange},
Mode: Append,
}
return return
} }
@ -250,22 +189,21 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
return return
} }
cnt = BuilderContent{ cnt = BuilderContent{
treeHeadIds: ot.tree.Heads(), TreeHeadIds: ot.tree.Heads(),
aclHeadId: ot.aclList.Head().Id, AclHeadId: ot.aclList.Head().Id,
snapshotBaseId: ot.tree.RootId(), SnapshotBaseId: ot.tree.RootId(),
currentReadKeyHash: state.CurrentReadKeyHash(), CurrentReadKeyHash: state.CurrentReadKeyHash(),
identity: content.Identity, Identity: content.Identity,
isSnapshot: content.IsSnapshot, IsSnapshot: content.IsSnapshot,
signingKey: content.Key, SigningKey: content.Key,
readKey: readKey, ReadKey: readKey,
content: content.Proto, Content: content.Data,
} }
return return
} }
func (ot *objectTree) AddRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (addResult AddResult, err error) { func (ot *objectTree) AddRawChanges(ctx context.Context, rawChanges ...*treechangeproto.RawTreeChangeWithId) (addResult AddResult, err error) {
var mode Mode addResult, err = ot.addRawChanges(ctx, rawChanges...)
mode, addResult, err = ot.addRawChanges(ctx, rawChanges...)
if err != nil { if err != nil {
return return
} }
@ -283,26 +221,10 @@ func (ot *objectTree) AddRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
// setting heads // setting heads
err = ot.treeStorage.SetHeads(ot.tree.Heads()) err = ot.treeStorage.SetHeads(ot.tree.Heads())
if err != nil {
return
}
if ot.updateListener == nil {
return
}
switch mode {
case Append:
ot.updateListener.Update(ot)
case Rebuild:
ot.updateListener.Rebuild(ot)
default:
break
}
return return
} }
func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.RawChange) (mode Mode, addResult AddResult, err error) { func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*treechangeproto.RawTreeChangeWithId) (addResult AddResult, err error) {
// resetting buffers // resetting buffers
ot.tmpChangesBuf = ot.tmpChangesBuf[:0] ot.tmpChangesBuf = ot.tmpChangesBuf[:0]
ot.notSeenIdxBuf = ot.notSeenIdxBuf[:0] ot.notSeenIdxBuf = ot.notSeenIdxBuf[:0]
@ -329,7 +251,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
} }
var change *Change var change *Change
change, err = ot.changeBuilder.ConvertFromRawAndVerify(ch) change, err = ot.changeBuilder.ConvertFromRaw(ch, true)
if err != nil { if err != nil {
return return
} }
@ -354,7 +276,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
// returns changes that we added to the tree as attached this round // returns changes that we added to the tree as attached this round
// they can include not only the changes that were added now, // they can include not only the changes that were added now,
// but also the changes that were previously in the tree // but also the changes that were previously in the tree
getAddedChanges := func(toConvert []*Change) (added []*aclpb.RawChange, err error) { getAddedChanges := func(toConvert []*Change) (added []*treechangeproto.RawTreeChangeWithId, err error) {
alreadyConverted := make(map[*Change]struct{}) alreadyConverted := make(map[*Change]struct{})
// first we see if we have already unmarshalled those changes // first we see if we have already unmarshalled those changes
@ -379,7 +301,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
for _, ch := range toConvert { for _, ch := range toConvert {
// if we got some changes that we need to convert to raw // if we got some changes that we need to convert to raw
if _, exists := alreadyConverted[ch]; !exists { if _, exists := alreadyConverted[ch]; !exists {
var raw *aclpb.RawChange var raw *treechangeproto.RawTreeChangeWithId
raw, err = ot.changeBuilder.BuildRaw(ch) raw, err = ot.changeBuilder.BuildRaw(ch)
if err != nil { if err != nil {
return return
@ -421,7 +343,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
ot.rebuildFromStorage(nil) ot.rebuildFromStorage(nil)
return return
} }
var added []*aclpb.RawChange var added []*treechangeproto.RawTreeChangeWithId
added, err = getAddedChanges(nil) added, err = getAddedChanges(nil)
// we shouldn't get any error in this case // we shouldn't get any error in this case
if err != nil { if err != nil {
@ -457,7 +379,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, rawChanges ...*aclpb.Ra
err = ErrHasInvalidChanges err = ErrHasInvalidChanges
return return
} }
var added []*aclpb.RawChange var added []*treechangeproto.RawTreeChangeWithId
added, err = getAddedChanges(treeChangesAdded) added, err = getAddedChanges(treeChangesAdded)
if err != nil { if err != nil {
// that means that some unattached changes were somehow corrupted in memory // that means that some unattached changes were somehow corrupted in memory
@ -488,17 +410,21 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
ot.tree.Iterate(ot.tree.RootId(), func(c *Change) (isContinue bool) { ot.tree.Iterate(ot.tree.RootId(), func(c *Change) (isContinue bool) {
var model any var model any
if c.ParsedModel != nil { if c.Model != nil {
return iterate(c) return iterate(c)
} }
readKey, exists := ot.keys[c.Content.CurrentReadKeyHash] // if this is a root change
if c.Id == ot.id {
return iterate(c)
}
readKey, exists := ot.keys[c.ReadKeyHash]
if !exists { if !exists {
err = list.ErrNoReadKey err = list.ErrNoReadKey
return false return false
} }
var decrypted []byte var decrypted []byte
decrypted, err = readKey.Decrypt(c.Content.GetChangesData()) decrypted, err = readKey.Decrypt(c.Data)
if err != nil { if err != nil {
return false return false
} }
@ -508,7 +434,7 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
return false return false
} }
c.ParsedModel = model c.Model = model
return iterate(c) return iterate(c)
}) })
return return
@ -553,7 +479,7 @@ func (ot *objectTree) SnapshotPath() []string {
return path return path
} }
func (ot *objectTree) ChangesAfterCommonSnapshot(theirPath, theirHeads []string) ([]*aclpb.RawChange, error) { func (ot *objectTree) ChangesAfterCommonSnapshot(theirPath, theirHeads []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
var ( var (
needFullDocument = len(theirPath) == 0 needFullDocument = len(theirPath) == 0
ourPath = ot.SnapshotPath() ourPath = ot.SnapshotPath()
@ -577,11 +503,11 @@ func (ot *objectTree) ChangesAfterCommonSnapshot(theirPath, theirHeads []string)
} }
} }
func (ot *objectTree) getChangesFromTree(theirHeads []string) (rawChanges []*aclpb.RawChange, err error) { func (ot *objectTree) getChangesFromTree(theirHeads []string) (rawChanges []*treechangeproto.RawTreeChangeWithId, err error) {
return ot.rawChangeLoader.LoadFromTree(ot.tree, theirHeads) return ot.rawChangeLoader.LoadFromTree(ot.tree, theirHeads)
} }
func (ot *objectTree) getChangesFromDB(commonSnapshot string, theirHeads []string) (rawChanges []*aclpb.RawChange, err error) { func (ot *objectTree) getChangesFromDB(commonSnapshot string, theirHeads []string) (rawChanges []*treechangeproto.RawTreeChangeWithId, err error) {
return ot.rawChangeLoader.LoadFromStorage(commonSnapshot, ot.tree.headIds, theirHeads) return ot.rawChangeLoader.LoadFromStorage(commonSnapshot, ot.tree.headIds, theirHeads)
} }

View File

@ -2,11 +2,10 @@ package tree
import ( import (
"context" "context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/acllistbuilder"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"testing" "testing"
@ -14,8 +13,26 @@ import (
type mockChangeCreator struct{} type mockChangeCreator struct{}
func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *aclpb.RawChange { func (c *mockChangeCreator) createRoot(id, aclId string) *treechangeproto.RawTreeChangeWithId {
aclChange := &aclpb.Change{ aclChange := &treechangeproto.RootChange{
AclHeadId: aclId,
}
res, _ := aclChange.Marshal()
raw := &treechangeproto.RawTreeChange{
Payload: res,
Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id,
}
}
func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.TreeChange{
TreeHeadIds: prevIds, TreeHeadIds: prevIds,
AclHeadId: aclId, AclHeadId: aclId,
SnapshotBaseId: snapshotId, SnapshotBaseId: snapshotId,
@ -23,22 +40,22 @@ func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot b
IsSnapshot: isSnapshot, IsSnapshot: isSnapshot,
} }
res, _ := aclChange.Marshal() res, _ := aclChange.Marshal()
return &aclpb.RawChange{
raw := &treechangeproto.RawTreeChange{
Payload: res, Payload: res,
Signature: nil, Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id, Id: id,
} }
} }
func (c *mockChangeCreator) createNewTreeStorage(treeId, aclListId, aclHeadId, firstChangeId string) storage.TreeStorage { func (c *mockChangeCreator) createNewTreeStorage(treeId, aclHeadId string) storage.TreeStorage {
firstChange := c.createRaw(firstChangeId, aclHeadId, "", true) root := c.createRoot(treeId, aclHeadId)
header := &aclpb.Header{ treeStorage, _ := storage.NewInMemoryTreeStorage(treeId, root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
FirstId: firstChangeId,
AclListId: aclListId,
WorkspaceId: "",
DocType: aclpb.Header_DocTree,
}
treeStorage, _ := storage.NewInMemoryTreeStorage(treeId, header, []string{firstChangeId}, []*aclpb.RawChange{firstChange})
return treeStorage return treeStorage
} }
@ -46,19 +63,23 @@ type mockChangeBuilder struct {
originalBuilder ChangeBuilder originalBuilder ChangeBuilder
} }
func (c *mockChangeBuilder) ConvertFromRaw(rawChange *aclpb.RawChange) (ch *Change, err error) { func (c *mockChangeBuilder) BuildInitialContent(payload InitialContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error) {
return c.originalBuilder.ConvertFromRaw(rawChange)
}
func (c *mockChangeBuilder) ConvertFromRawAndVerify(rawChange *aclpb.RawChange) (ch *Change, err error) {
return c.originalBuilder.ConvertFromRaw(rawChange)
}
func (c *mockChangeBuilder) BuildContent(payload BuilderContent) (ch *Change, raw *aclpb.RawChange, err error) {
panic("implement me") panic("implement me")
} }
func (c *mockChangeBuilder) BuildRaw(ch *Change) (raw *aclpb.RawChange, err error) { func (c *mockChangeBuilder) SetRootRawChange(rawIdChange *treechangeproto.RawTreeChangeWithId) {
c.originalBuilder.SetRootRawChange(rawIdChange)
}
func (c *mockChangeBuilder) ConvertFromRaw(rawChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error) {
return c.originalBuilder.ConvertFromRaw(rawChange, false)
}
func (c *mockChangeBuilder) BuildContent(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error) {
panic("implement me")
}
func (c *mockChangeBuilder) BuildRaw(ch *Change) (raw *treechangeproto.RawTreeChangeWithId, err error) {
return c.originalBuilder.BuildRaw(ch) return c.originalBuilder.BuildRaw(ch)
} }
@ -84,7 +105,7 @@ func prepareACLList(t *testing.T) list.ACLList {
st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml") st, err := acllistbuilder.NewListStorageWithTestName("userjoinexample.yml")
require.NoError(t, err, "building storage should not result in error") require.NoError(t, err, "building storage should not result in error")
aclList, err := list.BuildACLList(signingkey.NewEDPubKeyDecoder(), st) aclList, err := list.BuildACLList(st)
require.NoError(t, err, "building acl list should be without error") require.NoError(t, err, "building acl list should be without error")
return aclList return aclList
@ -92,15 +113,15 @@ func prepareACLList(t *testing.T) list.ACLList {
func prepareTreeContext(t *testing.T, aclList list.ACLList) testTreeContext { func prepareTreeContext(t *testing.T, aclList list.ACLList) testTreeContext {
changeCreator := &mockChangeCreator{} changeCreator := &mockChangeCreator{}
treeStorage := changeCreator.createNewTreeStorage("treeId", aclList.ID(), aclList.Head().Id, "0") treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
root, _ := treeStorage.Root()
changeBuilder := &mockChangeBuilder{ changeBuilder := &mockChangeBuilder{
originalBuilder: newChangeBuilder(nil), originalBuilder: newChangeBuilder(nil, root),
} }
deps := objectTreeDeps{ deps := objectTreeDeps{
changeBuilder: changeBuilder, changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(treeStorage, changeBuilder), treeBuilder: newTreeBuilder(treeStorage, changeBuilder),
treeStorage: treeStorage, treeStorage: treeStorage,
updateListener: nil,
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder), rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
validator: &mockChangeValidator{}, validator: &mockChangeValidator{},
aclList: aclList, aclList: aclList,
@ -136,7 +157,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
} }
@ -177,7 +198,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("0", aclList.Head().Id, "", true, ""), changeCreator.createRaw("0", aclList.Head().Id, "", true, ""),
} }
res, err := objTree.AddRawChanges(context.Background(), rawChanges...) res, err := objTree.AddRawChanges(context.Background(), rawChanges...)
@ -197,7 +218,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
} }
res, err := objTree.AddRawChanges(context.Background(), rawChanges...) res, err := objTree.AddRawChanges(context.Background(), rawChanges...)
@ -219,7 +240,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
@ -263,7 +284,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
@ -282,7 +303,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
@ -356,7 +377,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
@ -432,7 +453,7 @@ func TestObjectTree(t *testing.T) {
changeCreator := ctx.changeCreator changeCreator := ctx.changeCreator
objTree := ctx.objTree objTree := ctx.objTree
rawChanges := []*aclpb.RawChange{ rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"), changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"), changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
@ -441,7 +462,7 @@ func TestObjectTree(t *testing.T) {
require.NoError(t, err, "adding changes should be without error") require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "3", objTree.Root().Id) require.Equal(t, "3", objTree.Root().Id)
rawChanges = []*aclpb.RawChange{ rawChanges = []*treechangeproto.RawTreeChangeWithId{
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"), changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"), changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"), changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),

View File

@ -0,0 +1,151 @@
package tree
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/symmetric"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
"go.uber.org/zap"
"math/rand"
"time"
)
type ObjectTreeCreatePayload struct {
SignKey signingkey.PrivKey
ChangeType string
SpaceId string
Identity []byte
}
func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (ObjectTree, error) {
rootChange, err := treeStorage.Root()
if err != nil {
return nil, err
}
deps := defaultObjectTreeDeps(rootChange, treeStorage, aclList)
return buildObjectTree(deps)
}
func CreateDerivedObjectTree(
payload ObjectTreeCreatePayload,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
return createObjectTree(payload, 0, nil, aclList, createStorage)
}
func CreateObjectTree(
payload ObjectTreeCreatePayload,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
bytes := make([]byte, 32)
_, err = rand.Read(bytes)
if err != nil {
return
}
return createObjectTree(payload, time.Now().UnixNano(), bytes, aclList, createStorage)
}
func createObjectTree(
payload ObjectTreeCreatePayload,
timestamp int64,
seed []byte,
aclList list.ACLList,
createStorage storage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
aclList.RLock()
var (
deps = defaultObjectTreeDeps(nil, nil, aclList)
aclHeadId = aclList.Head().Id
)
aclList.RUnlock()
if err != nil {
return
}
cnt := InitialContent{
AclHeadId: aclHeadId,
Identity: payload.Identity,
SigningKey: payload.SignKey,
SpaceId: payload.SpaceId,
ChangeType: payload.ChangeType,
Timestamp: timestamp,
Seed: seed,
}
_, raw, err := deps.changeBuilder.BuildInitialContent(cnt)
if err != nil {
return
}
deps.changeBuilder.SetRootRawChange(raw)
// create storage
st, err := createStorage(storage.TreeStorageCreatePayload{
TreeId: raw.Id,
RootRawChange: raw,
Changes: []*treechangeproto.RawTreeChangeWithId{raw},
Heads: []string{raw.Id},
})
if err != nil {
return
}
deps.treeStorage = st
return buildObjectTree(deps)
}
func buildObjectTree(deps objectTreeDeps) (ObjectTree, error) {
objTree := &objectTree{
treeStorage: deps.treeStorage,
treeBuilder: deps.treeBuilder,
validator: deps.validator,
aclList: deps.aclList,
changeBuilder: deps.changeBuilder,
rawChangeLoader: deps.rawChangeLoader,
tree: nil,
keys: make(map[uint64]*symmetric.Key),
tmpChangesBuf: make([]*Change, 0, 10),
difSnapshotBuf: make([]*treechangeproto.RawTreeChangeWithId, 0, 10),
notSeenIdxBuf: make([]int, 0, 10),
newSnapshotsBuf: make([]*Change, 0, 10),
}
err := objTree.rebuildFromStorage(nil)
if err != nil {
return nil, err
}
storageHeads, err := objTree.treeStorage.Heads()
if err != nil {
return nil, err
}
// comparing rebuilt heads with heads in storage
// in theory it can happen that we didn't set heads because the process has crashed
// therefore we want to set them later
if !slice.UnsortedEquals(storageHeads, objTree.tree.Heads()) {
log.With(zap.Strings("storage", storageHeads), zap.Strings("rebuilt", objTree.tree.Heads())).
Errorf("the heads in storage and objTree are different")
err = objTree.treeStorage.SetHeads(objTree.tree.Heads())
if err != nil {
return nil, err
}
}
objTree.id, err = objTree.treeStorage.ID()
if err != nil {
return nil, err
}
objTree.root, err = objTree.treeStorage.Root()
if err != nil {
return nil, err
}
// verifying root
_, err = objTree.changeBuilder.ConvertFromRaw(objTree.root, true)
if err != nil {
return nil, err
}
return objTree, nil
}

View File

@ -2,8 +2,8 @@ package tree
import ( import (
"context" "context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage" "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treechangeproto"
"time" "time"
) )
@ -18,7 +18,7 @@ type rawChangeLoader struct {
type rawCacheEntry struct { type rawCacheEntry struct {
change *Change change *Change
rawChange *aclpb.RawChange rawChange *treechangeproto.RawTreeChangeWithId
position int position int
} }
@ -29,15 +29,15 @@ func newRawChangeLoader(treeStorage storage.TreeStorage, changeBuilder ChangeBui
} }
} }
func (r *rawChangeLoader) LoadFromTree(t *Tree, breakpoints []string) ([]*aclpb.RawChange, error) { func (r *rawChangeLoader) LoadFromTree(t *Tree, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
var stack []*Change var stack []*Change
for _, h := range t.headIds { for _, h := range t.headIds {
stack = append(stack, t.attached[h]) stack = append(stack, t.attached[h])
} }
convert := func(chs []*Change) (rawChanges []*aclpb.RawChange, err error) { convert := func(chs []*Change) (rawChanges []*treechangeproto.RawTreeChangeWithId, err error) {
for _, ch := range chs { for _, ch := range chs {
var raw *aclpb.RawChange var raw *treechangeproto.RawTreeChangeWithId
raw, err = r.changeBuilder.BuildRaw(ch) raw, err = r.changeBuilder.BuildRaw(ch)
if err != nil { if err != nil {
return return
@ -95,7 +95,7 @@ func (r *rawChangeLoader) LoadFromTree(t *Tree, breakpoints []string) ([]*aclpb.
return convert(results) return convert(results)
} }
func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoints []string) ([]*aclpb.RawChange, error) { func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
// resetting cache // resetting cache
r.cache = make(map[string]rawCacheEntry) r.cache = make(map[string]rawCacheEntry)
defer func() { defer func() {
@ -162,7 +162,7 @@ func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoi
// preparing first pass // preparing first pass
r.idStack = append(r.idStack, heads...) r.idStack = append(r.idStack, heads...)
var buffer []*aclpb.RawChange var buffer []*treechangeproto.RawTreeChangeWithId
rootVisited := dfs(commonSnapshot, heads, 0, rootVisited := dfs(commonSnapshot, heads, 0,
func(counter int, mapExists bool) bool { func(counter int, mapExists bool) bool {
@ -203,7 +203,7 @@ func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoi
}) })
// discarding visited // discarding visited
buffer = discardFromSlice(buffer, func(change *aclpb.RawChange) bool { buffer = discardFromSlice(buffer, func(change *treechangeproto.RawTreeChangeWithId) bool {
return change == nil return change == nil
}) })
@ -219,7 +219,7 @@ func (r *rawChangeLoader) loadEntry(id string) (entry rawCacheEntry, err error)
return return
} }
change, err := r.changeBuilder.ConvertFromRaw(rawChange) change, err := r.changeBuilder.ConvertFromRaw(rawChange, false)
if err != nil { if err != nil {
return return
} }

View File

@ -0,0 +1,17 @@
package tree
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
)
func ValidateRawTree(payload storage.TreeStorageCreatePayload, aclList list.ACLList) (err error) {
provider := storage.NewInMemoryTreeStorageProvider()
treeStorage, err := provider.CreateTreeStorage(payload)
if err != nil {
return
}
_, err = BuildObjectTree(treeStorage, aclList)
return
}

View File

@ -2,12 +2,11 @@ package tree
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey" "github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
"github.com/gogo/protobuf/proto"
) )
type SignableChangeContent struct { type SignableChangeContent struct {
Proto proto.Marshaler Data []byte
Key signingkey.PrivKey Key signingkey.PrivKey
Identity string Identity []byte
IsSnapshot bool IsSnapshot bool
} }

View File

@ -129,7 +129,7 @@ func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) {
return nil, err return nil, err
} }
ch, err = tb.builder.ConvertFromRawAndVerify(change) ch, err = tb.builder.ConvertFromRaw(change, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -59,7 +59,7 @@ func (t *Tree) Graph(parser DescriptionParser) (data string, err error) {
label := fmt.Sprintf("Id: %s\nOrd: %s\nTime: %s\nChanges: %s\n", label := fmt.Sprintf("Id: %s\nOrd: %s\nTime: %s\nChanges: %s\n",
shortId, shortId,
ord, ord,
time.Unix(c.Content.Timestamp, 0).Format("02.01.06 15:04:05"), time.Unix(c.Timestamp, 0).Format("02.01.06 15:04:05"),
strings.Join(chSymbs, ","), strings.Join(chSymbs, ","),
) )
n.SetLabel(label) n.SetLabel(label)

View File

@ -1,91 +0,0 @@
package tree
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/cid"
"github.com/gogo/protobuf/proto"
"time"
)
func CreateNewTreeStorage(
acc *account.AccountData,
aclList list.ACLList,
content proto.Marshaler,
create storage.TreeStorageCreatorFunc) (thr storage.TreeStorage, err error) {
state := aclList.ACLState()
change := &aclpb.Change{
AclHeadId: aclList.Head().Id,
CurrentReadKeyHash: state.CurrentReadKeyHash(),
Timestamp: int64(time.Now().Nanosecond()),
Identity: acc.Identity,
IsSnapshot: true,
}
marshalledData, err := content.Marshal()
if err != nil {
return
}
readKey, err := state.CurrentReadKey()
if err != nil {
return
}
encrypted, err := readKey.Encrypt(marshalledData)
if err != nil {
return
}
change.ChangesData = encrypted
fullMarshalledChange, err := proto.Marshal(change)
if err != nil {
return
}
signature, err := acc.SignKey.Sign(fullMarshalledChange)
if err != nil {
return
}
changeId, err := cid.NewCIDFromBytes(fullMarshalledChange)
if err != nil {
return
}
rawChange := &aclpb.RawChange{
Payload: fullMarshalledChange,
Signature: signature,
Id: changeId,
}
header, treeId, err := createTreeHeaderAndId(rawChange, aclpb.Header_DocTree, aclList.ID())
if err != nil {
return
}
return create(storage.TreeStorageCreatePayload{
TreeId: treeId,
Header: header,
Changes: []*aclpb.RawChange{rawChange},
Heads: []string{rawChange.Id},
})
}
func createTreeHeaderAndId(change *aclpb.RawChange, treeType aclpb.HeaderDocType, aclListId string) (header *aclpb.Header, treeId string, err error) {
header = &aclpb.Header{
FirstId: change.Id,
DocType: treeType,
AclListId: aclListId,
}
marshalledHeader, err := proto.Marshal(header)
if err != nil {
return
}
treeId, err = cid.NewCIDFromBytes(marshalledHeader)
return
}

View File

@ -0,0 +1,55 @@
syntax = "proto3";
package treechange;
option go_package = "pkg/acl/treechangeproto";
// RootChange is a root of a tree
message RootChange {
// AclHeadId is a cid of latest acl record at the time of tree creation
string aclHeadId = 1;
// SpaceId is an id of space where the document is placed
string spaceId = 2;
// ChangeType is a type of tree which this RootChange is a root of
string changeType = 3;
// Timestamp is this change creation timestamp
int64 timestamp = 4;
// Seed is a random bytes to make root change unique
bytes seed = 5;
// Identity is a public key of the tree's creator
bytes identity = 6;
}
// TreeChange is a change of a tree
message TreeChange {
// TreeHeadIds are previous ids for this TreeChange
repeated string treeHeadIds = 1;
// AclHeadId is a cid of latest acl record at the time of this change
string aclHeadId = 2;
// SnapshotBaseId is a snapshot (root) of the tree where this change is added
string snapshotBaseId = 3;
// ChangesData is an arbitrary payload to be read by the client
bytes changesData = 4;
// CurrentReadKeyHash is the hash of the read key which is used to encrypt this change
uint64 currentReadKeyHash = 5;
// Timestamp is this change creation timestamp
int64 timestamp = 6;
// Identity is a public key with which the raw payload of this change is signed
bytes identity = 7;
// IsSnapshot indicates whether this change contains a snapshot of state
bool isSnapshot = 8;
}
// RawTreeChange is a marshalled TreeChange (or RootChange) payload and a signature of this payload
message RawTreeChange {
// Payload is a byte payload containing TreeChange
bytes payload = 1;
// Signature is a signature made by identity indicated in the TreeChange payload
bytes signature = 2;
}
// RawTreeChangeWithId is a marshalled RawTreeChange with CID
message RawTreeChangeWithId {
// RawChange is a byte payload of RawTreeChange
bytes rawChange = 1;
// Id is a cid made from rawChange payload
string id = 2;
}

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,9 @@ type LoadFunc func(ctx context.Context, id string) (value Object, err error)
type Option func(*oCache) type Option func(*oCache)
var WithLogServiceName = func(name string) Option { var WithLogger = func(l *zap.SugaredLogger) Option {
return func(cache *oCache) { return func(cache *oCache) {
cache.log = cache.log.With("service_name", name) cache.log = l
} }
} }
@ -45,6 +45,12 @@ var WithGCPeriod = func(gcPeriod time.Duration) Option {
} }
} }
var WithRefCounter = func(enable bool) Option {
return func(cache *oCache) {
cache.noRefCounter = !enable
}
}
func New(loadFunc LoadFunc, opts ...Option) OCache { func New(loadFunc LoadFunc, opts ...Option) OCache {
c := &oCache{ c := &oCache{
data: make(map[string]*entry), data: make(map[string]*entry),
@ -69,10 +75,13 @@ type Object interface {
} }
type ObjectLocker interface { type ObjectLocker interface {
Object
Locked() bool Locked() bool
} }
type ObjectLastUsage interface {
LastUsage() time.Time
}
type entry struct { type entry struct {
id string id string
lastUsage time.Time lastUsage time.Time
@ -99,7 +108,7 @@ type OCache interface {
// When 'loadFunc' returns a non-nil error, an object will not be stored to cache // When 'loadFunc' returns a non-nil error, an object will not be stored to cache
Get(ctx context.Context, id string) (value Object, err error) Get(ctx context.Context, id string) (value Object, err error)
// Pick returns value if it's presents in cache (will not call loadFunc) // Pick returns value if it's presents in cache (will not call loadFunc)
Pick(id string) (value Object, err error) Pick(ctx context.Context, id string) (value Object, err error)
// Add adds new object to cache // Add adds new object to cache
// Returns error when object exists // Returns error when object exists
Add(id string, value Object) (err error) Add(id string, value Object) (err error)
@ -121,15 +130,16 @@ type OCache interface {
} }
type oCache struct { type oCache struct {
mu sync.Mutex mu sync.Mutex
data map[string]*entry data map[string]*entry
loadFunc LoadFunc loadFunc LoadFunc
timeNow func() time.Time timeNow func() time.Time
ttl time.Duration ttl time.Duration
gc time.Duration gc time.Duration
closed bool closed bool
closeCh chan struct{} closeCh chan struct{}
log *zap.SugaredLogger log *zap.SugaredLogger
noRefCounter bool
} }
func (c *oCache) Get(ctx context.Context, id string) (value Object, err error) { func (c *oCache) Get(ctx context.Context, id string) (value Object, err error) {
@ -152,7 +162,9 @@ func (c *oCache) Get(ctx context.Context, id string) (value Object, err error) {
c.data[id] = e c.data[id] = e
} }
e.lastUsage = c.timeNow() e.lastUsage = c.timeNow()
e.refCount++ if !c.noRefCounter {
e.refCount++
}
c.mu.Unlock() c.mu.Unlock()
if load { if load {
@ -166,13 +178,18 @@ func (c *oCache) Get(ctx context.Context, id string) (value Object, err error) {
return e.value, e.loadErr return e.value, e.loadErr
} }
func (c *oCache) Pick(id string) (value Object, err error) { func (c *oCache) Pick(ctx context.Context, id string) (value Object, err error) {
c.mu.Lock() c.mu.Lock()
val, ok := c.data[id] val, ok := c.data[id]
c.mu.Unlock() c.mu.Unlock()
if !ok { if !ok {
return nil, ErrNotExists return nil, ErrNotExists
} }
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-val.load:
}
<-val.load <-val.load
return val.value, val.loadErr return val.value, val.loadErr
} }
@ -198,7 +215,7 @@ func (c *oCache) Release(id string) bool {
return false return false
} }
if e, ok := c.data[id]; ok { if e, ok := c.data[id]; ok {
if e.refCount > 0 { if !c.noRefCounter && e.refCount > 0 {
e.refCount-- e.refCount--
return true return true
} }
@ -307,7 +324,11 @@ func (c *oCache) GC() {
deadline := c.timeNow().Add(-c.ttl) deadline := c.timeNow().Add(-c.ttl)
var toClose []*entry var toClose []*entry
for k, e := range c.data { for k, e := range c.data {
if !e.locked() && e.refCount <= 0 && e.lastUsage.Before(deadline) { lu := e.lastUsage
if lug, ok := e.value.(ObjectLastUsage); ok {
lu = lug.LastUsage()
}
if !e.locked() && e.refCount <= 0 && lu.Before(deadline) {
delete(c.data, k) delete(c.data, k)
toClose = append(toClose, e) toClose = append(toClose, e)
} }

View File

@ -1,73 +0,0 @@
package account
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/encryptionkey"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/keys/asymmetric/signingkey"
)
const CName = "account"
type Service interface {
Account() *account.AccountData
}
type service struct {
accountData *account.AccountData
peerId string
}
func (s *service) Account() *account.AccountData {
return s.accountData
}
type StaticAccount struct {
SigningKey string `yaml:"signingKey"`
EncryptionKey string `yaml:"encryptionKey"`
}
func New() app.Component {
return &service{}
}
func (s *service) Init(ctx context.Context, a *app.App) (err error) {
cfg := a.MustComponent(config.CName).(*config.Config)
// decoding our keys
privateEncryptionDecoder := encryptionkey.NewRSAPrivKeyDecoder()
privateSigningDecoder := signingkey.NewEDPrivKeyDecoder()
publicSigningDecoder := signingkey.NewEDPubKeyDecoder()
acc := cfg.Account
decodedEncryptionKey, err := privateEncryptionDecoder.DecodeFromString(acc.EncryptionKey)
if err != nil {
return err
}
decodedSigningKey, err := privateSigningDecoder.DecodeFromString(acc.SigningKey)
if err != nil {
return err
}
signKey := decodedSigningKey.(signingkey.PrivKey)
identity, err := publicSigningDecoder.EncodeToString(signKey.GetPublic())
if err != nil {
return err
}
// TODO: using acl lib format basically, but we should simplify this
s.accountData = &account.AccountData{
Identity: identity,
SignKey: signKey,
EncKey: decodedEncryptionKey.(encryptionkey.PrivKey),
Decoder: signingkey.NewEDPubKeyDecoder(),
}
s.peerId = acc.PeerId
return nil
}
func (s *service) Name() (name string) {
return CName
}

View File

@ -1,137 +0,0 @@
package api
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/document"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache"
"go.uber.org/zap"
"io"
"net/http"
"time"
)
const CName = "APIService"
var log = logger.NewNamed("api")
func New() app.Component {
return &service{}
}
type service struct {
treeCache treecache.Service
documentService document.Service
srv *http.Server
cfg *config.Config
}
func (s *service) Init(ctx context.Context, a *app.App) (err error) {
s.treeCache = a.MustComponent(treecache.CName).(treecache.Service)
s.documentService = a.MustComponent(document.CName).(document.Service)
s.cfg = a.MustComponent(config.CName).(*config.Config)
return nil
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) Run(ctx context.Context) (err error) {
defer func() {
if err == nil {
log.With(zap.String("port", s.cfg.APIServer.Port)).Info("api server started running")
}
}()
s.srv = &http.Server{
Addr: fmt.Sprintf(":%s", s.cfg.APIServer.Port),
}
mux := http.NewServeMux()
mux.HandleFunc("/treeDump", s.treeDump)
mux.HandleFunc("/createDocumentTree", s.createDocumentTree)
mux.HandleFunc("/appendDocument", s.appendDocument)
s.srv.Handler = mux
go s.runServer()
return nil
}
func (s *service) runServer() {
err := s.srv.ListenAndServe()
if err != nil {
log.With(zap.Error(err)).Error("could not run api server")
}
}
func (s *service) Close(ctx context.Context) (err error) {
return s.srv.Shutdown(ctx)
}
func (s *service) treeDump(w http.ResponseWriter, req *http.Request) {
var (
query = req.URL.Query()
treeId = query.Get("treeId")
dump string
err error
)
err = s.treeCache.Do(context.Background(), treeId, func(obj interface{}) error {
t := obj.(tree.ObjectTree)
dump, err = t.DebugDump()
if err != nil {
return err
}
return nil
})
if err != nil {
sendText(w, http.StatusInternalServerError, err.Error())
return
}
sendText(w, http.StatusOK, dump)
}
func (s *service) createDocumentTree(w http.ResponseWriter, req *http.Request) {
var (
query = req.URL.Query()
text = query.Get("text")
aclListId = query.Get("aclListId")
)
timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30)
treeId, err := s.documentService.CreateDocumentTree(timeoutCtx, aclListId, text)
cancel()
if err != nil {
sendText(w, http.StatusInternalServerError, err.Error())
return
}
sendText(w, http.StatusOK, treeId)
}
func (s *service) appendDocument(w http.ResponseWriter, req *http.Request) {
var (
query = req.URL.Query()
text = query.Get("text")
treeId = query.Get("treeId")
)
timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30)
err := s.documentService.UpdateDocumentTree(timeoutCtx, treeId, text)
cancel()
if err != nil {
sendText(w, http.StatusInternalServerError, err.Error())
return
}
sendText(w, http.StatusOK, fmt.Sprintf("updated document with id: %s with text: %s", treeId, text))
}
func sendText(r http.ResponseWriter, code int, body string) {
r.Header().Set("Content-Type", "text/plain")
r.WriteHeader(code)
_, err := io.WriteString(r, fmt.Sprintf("%s\n", body))
if err != nil {
log.Error("writing response failed", zap.Error(err))
}
}

View File

@ -1,73 +0,0 @@
package configuration
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/config"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/net/pool"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/node"
"github.com/anytypeio/go-chash"
)
const CName = "configuration"
const (
partitionCount = 3000
replicationFactor = 3
)
var log = logger.NewNamed(CName)
type Service interface {
GetLast() Configuration
GetById(id string) Configuration
app.Component
}
type service struct {
accountId string
pool pool.Pool
last Configuration
}
func (s *service) Init(ctx context.Context, a *app.App) (err error) {
conf := a.MustComponent(config.CName).(*config.Config)
s.accountId = conf.Account.PeerId
s.pool = a.MustComponent(pool.CName).(pool.Pool)
configNodes := a.MustComponent(node.CName).(node.Service).Nodes()
config := &configuration{
id: "config",
accountId: s.accountId,
pool: s.pool,
}
if config.chash, err = chash.New(chash.Config{
PartitionCount: partitionCount,
ReplicationFactor: replicationFactor,
}); err != nil {
return
}
members := make([]chash.Member, 0, len(configNodes))
for _, n := range configNodes {
members = append(members, n)
}
if err = config.chash.AddMembers(members...); err != nil {
return
}
s.last = config
return
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) GetLast() Configuration {
return s.last
}
func (s *service) GetById(id string) Configuration {
//TODO implement me
panic("implement me")
}

View File

@ -1,219 +0,0 @@
package document
import (
"context"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/list"
testchanges "github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/proto"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/node"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/storage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/message"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache"
"github.com/anytypeio/go-anytype-infrastructure-experiments/syncproto"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
)
var CName = "DocumentService"
var log = logger.NewNamed("documentservice")
type service struct {
messageService message.Service
treeCache treecache.Service
account account.Service
storage storage.Service
// to create new documents we need to know all nodes
nodes []*node.Node
}
type Service interface {
UpdateDocumentTree(ctx context.Context, id, text string) error
CreateDocumentTree(ctx context.Context, aclTreeId string, text string) (id string, err error)
}
func New() app.Component {
return &service{}
}
func (s *service) Init(ctx context.Context, a *app.App) (err error) {
s.account = a.MustComponent(account.CName).(account.Service)
s.messageService = a.MustComponent(message.CName).(message.Service)
s.treeCache = a.MustComponent(treecache.CName).(treecache.Service)
s.storage = a.MustComponent(storage.CName).(storage.Service)
nodesService := a.MustComponent(node.CName).(node.Service)
s.nodes = nodesService.Nodes()
return nil
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) Run(ctx context.Context) (err error) {
syncData := s.storage.ImportedACLSyncData()
// we could have added a timeout or some additional logic,
// but let's just use the ACL id of the latest started node :-)
return s.messageService.SendToSpaceAsync("", syncproto.WrapACLList(
&syncproto.SyncACLList{Records: syncData.Records},
syncData.Header,
syncData.Id,
))
}
func (s *service) Close(ctx context.Context) (err error) {
return nil
}
func (s *service) UpdateDocumentTree(ctx context.Context, id, text string) (err error) {
var (
ch *aclpb.RawChange
header *aclpb.Header
snapshotPath []string
heads []string
)
log.With(zap.String("id", id), zap.String("text", text)).
Debug("updating document")
err = s.treeCache.Do(ctx, id, func(obj interface{}) error {
docTree, ok := obj.(tree.ObjectTree)
if !ok {
return fmt.Errorf("can't update acl trees with text")
}
docTree.Lock()
defer docTree.Unlock()
err = s.treeCache.Do(ctx, docTree.Header().AclListId, func(obj interface{}) error {
aclTree := obj.(list.ACLList)
aclTree.RLock()
defer aclTree.RUnlock()
content := createAppendTextChange(text)
signable := tree.SignableChangeContent{
Proto: content,
Key: s.account.Account().SignKey,
Identity: s.account.Account().Identity,
IsSnapshot: false,
}
ch, err = docTree.AddContent(ctx, signable)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
id = docTree.ID()
heads = docTree.Heads()
header = docTree.Header()
snapshotPath = docTree.SnapshotPath()
return nil
})
if err != nil {
return err
}
log.With(
zap.String("id", id),
zap.Strings("heads", heads),
zap.String("header", header.String())).
Debug("document updated in the database")
return s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{
Heads: heads,
Changes: []*aclpb.RawChange{ch},
SnapshotPath: snapshotPath,
}, header, id))
}
func (s *service) CreateDocumentTree(ctx context.Context, aclListId string, text string) (id string, err error) {
acc := s.account.Account()
var (
ch *aclpb.RawChange
header *aclpb.Header
snapshotPath []string
heads []string
)
err = s.treeCache.Do(ctx, aclListId, func(obj interface{}) error {
t := obj.(list.ACLList)
t.RLock()
defer t.RUnlock()
content := createInitialTextChange(text)
doc, err := tree.CreateNewTreeStorage(acc, t, content, s.storage.CreateTreeStorage)
if err != nil {
return err
}
id, err = doc.ID()
if err != nil {
return err
}
header, err = doc.Header()
if err != nil {
return err
}
heads = []string{header.FirstId}
snapshotPath = []string{header.FirstId}
ch, err = doc.GetRawChange(ctx, header.FirstId)
if err != nil {
return err
}
return nil
})
if err != nil {
return "", err
}
log.With(zap.String("id", id), zap.String("text", text)).
Debug("creating document")
err = s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{
Heads: heads,
Changes: []*aclpb.RawChange{ch},
SnapshotPath: snapshotPath,
}, header, id))
if err != nil {
return "", err
}
return id, err
}
func createInitialTextChange(text string) proto.Marshaler {
return &testchanges.PlainTextChangeData{
Content: []*testchanges.PlainTextChangeContent{
createAppendTextChangeContent(text),
},
Snapshot: &testchanges.PlainTextChangeSnapshot{Text: text},
}
}
func createAppendTextChange(text string) proto.Marshaler {
return &testchanges.PlainTextChangeData{
Content: []*testchanges.PlainTextChangeContent{
createAppendTextChangeContent(text),
},
}
}
func createAppendTextChangeContent(text string) *testchanges.PlainTextChangeContent {
return &testchanges.PlainTextChangeContent{
Value: &testchanges.PlainTextChangeContentValueOfTextAppend{
TextAppend: &testchanges.PlainTextChangeTextAppend{
Text: text,
},
},
}
}

Some files were not shown because too many files have changed in this diff Show More