WIP change creation logic
This commit is contained in:
parent
eea64a3014
commit
2f66bd3e9c
@ -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")
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
133
data/threadbuilder/userjoinexampleupdate.yml
Normal file
133
data/threadbuilder/userjoinexampleupdate.yml
Normal 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 ]
|
||||||
Loading…
x
Reference in New Issue
Block a user