WIP change creation logic

This commit is contained in:
mcrakhman 2022-07-07 11:11:34 +02:00 committed by Mikhail Iudin
parent eea64a3014
commit 2f66bd3e9c
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
5 changed files with 229 additions and 3 deletions

View File

@ -33,8 +33,7 @@ func (sb *ACLStateBuilder) Build() (*ACLState, error) {
return state, err return state, err
} }
func (sb *ACLStateBuilder) Init( func (sb *ACLStateBuilder) Init(tree *Tree) error {
tree *Tree) error {
root := tree.Root() root := tree.Root()
if !root.IsSnapshot { if !root.IsSnapshot {
return fmt.Errorf("root should always be a snapshot") return fmt.Errorf("root should always be a snapshot")

View File

@ -128,7 +128,7 @@ func (tb *ACLTreeBuilder) getACLHeads(heads []string) (aclTreeHeads []string, er
} }
precedingHeads, err := tb.getPrecedingACLHeads(head) precedingHeads, err := tb.getPrecedingACLHeads(head)
if err != nil { if err != nil {
return nil, err continue
} }
for _, aclHead := range precedingHeads { for _, aclHead := range precedingHeads {

View File

@ -2,11 +2,15 @@ package data
import ( import (
"fmt" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels"
"github.com/anytypeio/go-anytype-infrastructure-experiments/util/slice"
"github.com/gogo/protobuf/proto"
) )
type AccountData struct { type AccountData struct {
Identity string Identity string
SignKey threadmodels.SigningPrivKey
EncKey threadmodels.EncryptionPrivKey EncKey threadmodels.EncryptionPrivKey
} }
@ -33,6 +37,12 @@ const (
UpdateResultRebuild UpdateResultRebuild
) )
type CreateChangePayload struct {
ChangesData proto.Marshaler
ACLData *pb.ACLChangeACLData
Id string // TODO: this is just for testing, because id should be created automatically from content
}
func NewDocument( func NewDocument(
thread threadmodels.Thread, thread threadmodels.Thread,
stateProvider InitialStateProvider, stateProvider InitialStateProvider,
@ -53,6 +63,59 @@ func NewDocument(
} }
} }
func (d *Document) Create(payload *CreateChangePayload) error {
// TODO: add snapshot creation logic
marshalled, err := payload.ChangesData.Marshal()
if err != nil {
return err
}
encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash].
Encrypt(marshalled)
if err != nil {
return err
}
aclChange := &pb.ACLChange{
TreeHeadIds: d.docContext.fullTree.Heads(),
AclHeadIds: d.getACLHeads(),
SnapshotBaseId: d.docContext.fullTree.RootId(),
AclData: payload.ACLData,
ChangesData: encrypted,
CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash,
Timestamp: 0,
Identity: d.accountData.Identity,
}
// TODO: add CID creation logic based on content
ch := NewChange(payload.Id, aclChange)
ch.DecryptedDocumentChange = marshalled
fullMarshalledChange, err := proto.Marshal(aclChange)
if err != nil {
return err
}
signature, err := d.accountData.SignKey.Sign(fullMarshalledChange)
if err != nil {
return err
}
d.docContext.fullTree.AddFast(ch)
err = d.thread.AddChange(&threadmodels.RawChange{
Payload: marshalled,
Signature: signature,
Id: payload.Id,
})
if err != nil {
return err
}
d.thread.SetHeads([]string{ch.Id})
d.thread.SetMaybeHeads([]string{ch.Id})
return nil
}
func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) {
var treeChanges []*Change var treeChanges []*Change
@ -133,6 +196,35 @@ func (d *Document) Build() (DocumentState, error) {
return d.build(false) return d.build(false)
} }
// TODO: this should not be the responsibility of Document, move it somewhere else after testing
func (d *Document) getACLHeads() []string {
var aclTreeHeads []string
for _, head := range d.docContext.fullTree.Heads() {
if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
continue
}
precedingHeads := d.getPrecedingACLHeads(head)
for _, aclHead := range precedingHeads {
if slice.FindPos(aclTreeHeads, aclHead) != -1 {
continue
}
aclTreeHeads = append(aclTreeHeads, aclHead)
}
}
return aclTreeHeads
}
func (d *Document) getPrecedingACLHeads(head string) []string {
headChange := d.docContext.fullTree.attached[head]
if headChange.Content.GetAclData() != nil {
return []string{head}
} else {
return headChange.Content.AclHeadIds
}
}
func (d *Document) build(fromStart bool) (DocumentState, error) { func (d *Document) build(fromStart bool) (DocumentState, error) {
d.treeBuilder.Init() d.treeBuilder.Init()
d.aclTreeBuilder.Init() d.aclTreeBuilder.Init()
@ -143,6 +235,7 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
return nil, err return nil, err
} }
// TODO: remove this from context as this is used only to validate snapshot
d.docContext.aclTree, err = d.aclTreeBuilder.Build() d.docContext.aclTree, err = d.aclTreeBuilder.Build()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -64,6 +64,7 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) {
} }
func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) { func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) {
// TODO: we should do something like state copy probably
s = init s = init
d.tree.Iterate(fromId, func(c *Change) (isContinue bool) { d.tree.Iterate(fromId, func(c *Change) (isContinue bool) {
if c.Id == fromId { if c.Id == fromId {

View File

@ -0,0 +1,133 @@
thread:
author: A
changes:
- id: A.1.1
identity: A
aclSnapshot:
userStates:
- identity: A
encryptionKey: key.Enc.A
encryptedReadKeys: [key.Read.1]
permission: admin
snapshot:
text: "some text"
aclChanges:
- userAdd:
identity: A
permission: admin
encryptionKey: key.Enc.A
encryptedReadKeys: [key.Read.1]
changes:
- textAppend:
text: "some text"
readKey: key.Read.1
- id: A.1.2
identity: A
aclChanges:
- userInvite:
acceptKey: key.Sign.Onetime1
encryptionKey: key.Enc.Onetime1
encryptedReadKeys: [key.Read.1]
permissions: writer
- userAdd:
identity: C
permission: reader
encryptionKey: key.Enc.C
encryptedReadKeys: [ key.Read.1 ]
readKey: key.Read.1
- id: A.1.3
identity: A
changes:
- textAppend:
text: "second"
readKey: key.Read.1
- id: B.1.1
identity: B
aclChanges:
- userJoin:
identity: B
encryptionKey: key.Enc.B
acceptSignature: key.Sign.Onetime1
inviteId: A.1.2
encryptedReadKeys: [key.Read.1]
readKey: key.Read.1
- id: B.1.2
identity: B
changes:
- textAppend:
text: "first"
readKey: key.Read.1
- id: C.1.1
identity: C
changes:
- textAppend:
text: "third"
readKey: key.Read.1
keys:
Enc:
- A
- B
- C
- D
- Onetime1
Sign:
- A
- B
- C
- D
- Onetime1
Read:
- 1
graph:
- id: A.1.1
baseSnapshot: A.1.1
- id: A.1.2
baseSnapshot: A.1.1
aclHeads: [A.1.1]
treeHeads: [A.1.1]
- id: B.1.1
baseSnapshot: A.1.1
aclHeads: [A.1.2]
treeHeads: [A.1.2]
- id: B.1.2
baseSnapshot: A.1.1
aclHeads: [B.1.1]
treeHeads: [B.1.1]
- id: A.1.3 # this should be invalid, because it is based on one of the invalid changes
baseSnapshot: A.1.1
aclHeads: [B.1.1]
treeHeads: [B.1.2, C.1.1]
- id: C.1.1 # this should be invalid, because C is a reader
baseSnapshot: A.1.1
aclHeads: [B.1.1]
treeHeads: [B.1.1]
maybeHeads:
- "A.1.3"
updatedChanges:
- id: B.1.3
identity: B
changes:
- textAppend:
text: "second"
readKey: key.Read.1
- id: A.1.4
identity: A
aclChanges:
- userAdd:
identity: D
permission: writer
encryptionKey: key.Enc.D
encryptedReadKeys: [ key.Read.1 ]
changes:
- textAppend:
text: "second"
readKey: key.Read.1
updatedGraph:
- id: B.1.3
baseSnapshot: A.1.1
aclHeads: [ B.1.1 ]
treeHeads: [ B.1.2 ]
- id: A.1.4
baseSnapshot: A.1.1
aclHeads: [ B.1.1 ]
treeHeads: [ B.1.3 ]