WIP document update

This commit is contained in:
mcrakhman 2022-07-06 21:51:00 +02:00 committed by Mikhail Iudin
parent 2bd5bf742d
commit 0e62047498
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
6 changed files with 124 additions and 31 deletions

View File

@ -38,7 +38,7 @@ func (ch *Change) IsACLChange() bool {
return ch.Content.GetAclData() != nil return ch.Content.GetAclData() != nil
} }
func NewChange(id string, ch *pb.ACLChange) (*Change, error) { func NewChange(id string, ch *pb.ACLChange) *Change {
return &Change{ return &Change{
Next: nil, Next: nil,
PreviousIds: ch.TreeHeadIds, PreviousIds: ch.TreeHeadIds,
@ -46,10 +46,10 @@ func NewChange(id string, ch *pb.ACLChange) (*Change, error) {
Content: ch, Content: ch,
SnapshotId: ch.SnapshotBaseId, SnapshotId: ch.SnapshotBaseId,
IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil,
}, nil }
} }
func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) { func NewACLChange(id string, ch *pb.ACLChange) *Change {
return &Change{ return &Change{
Next: nil, Next: nil,
PreviousIds: ch.AclHeadIds, PreviousIds: ch.AclHeadIds,
@ -57,5 +57,5 @@ func NewACLChange(id string, ch *pb.ACLChange) (*Change, error) {
Content: ch, Content: ch,
SnapshotId: ch.SnapshotBaseId, SnapshotId: ch.SnapshotBaseId,
IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil, IsSnapshot: ch.GetAclData().GetAclSnapshot() != nil,
}, nil }
} }

View File

@ -14,13 +14,13 @@ type changeLoader struct {
identityKeys map[string]threadmodels.SigningPubKey identityKeys map[string]threadmodels.SigningPubKey
signingPubKeyDecoder threadmodels.SigningPubKeyDecoder signingPubKeyDecoder threadmodels.SigningPubKeyDecoder
thread threadmodels.Thread thread threadmodels.Thread
changeCreator func(id string, ch *pb.ACLChange) (*Change, error) changeCreator func(id string, ch *pb.ACLChange) *Change
} }
func newChangeLoader( func newChangeLoader(
thread threadmodels.Thread, thread threadmodels.Thread,
signingPubKeyDecoder threadmodels.SigningPubKeyDecoder, signingPubKeyDecoder threadmodels.SigningPubKeyDecoder,
changeCreator func(id string, ch *pb.ACLChange) (*Change, error)) *changeLoader { changeCreator func(id string, ch *pb.ACLChange) *Change) *changeLoader {
return &changeLoader{ return &changeLoader{
signingPubKeyDecoder: signingPubKeyDecoder, signingPubKeyDecoder: signingPubKeyDecoder,
thread: thread, thread: thread,
@ -48,23 +48,12 @@ func (c *changeLoader) loadChange(id string) (ch *Change, err error) {
return nil, err return nil, err
} }
aclChange := new(pb.ACLChange) aclChange, err := c.makeVerifiedACLChange(change)
// TODO: think what should we do with such cases, because this can be used by attacker to break our tree
if err = proto.Unmarshal(change.Payload, aclChange); err != nil {
return
}
var verified bool
verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature)
if err != nil { if err != nil {
return return nil, err
}
if !verified {
err = fmt.Errorf("the signature of the payload cannot be verified")
return
} }
ch, err = c.changeCreator(id, aclChange) ch = c.changeCreator(id, aclChange)
c.cache[id] = ch c.cache[id] = ch
return ch, nil return ch, nil
@ -81,3 +70,22 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer
} }
return identityKey.Verify(payload, signature) return identityKey.Verify(payload, signature)
} }
func (c *changeLoader) makeVerifiedACLChange(change *threadmodels.RawChange) (aclChange *pb.ACLChange, err error) {
aclChange = new(pb.ACLChange)
// TODO: think what should we do with such cases, because this can be used by attacker to break our tree
if err = proto.Unmarshal(change.Payload, aclChange); err != nil {
return
}
var verified bool
verified, err = c.verify(aclChange.Identity, change.Payload, change.Signature)
if err != nil {
return
}
if !verified {
err = fmt.Errorf("the signature of the payload cannot be verified")
return
}
return
}

View File

@ -1,7 +1,7 @@
package data package data
import ( import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb" "fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels"
) )
@ -28,10 +28,9 @@ type Document struct {
type UpdateResult int type UpdateResult int
const ( const (
UpdateResultAppend = iota UpdateResultNoAction = iota
UpdateResultAppend
UpdateResultRebuild UpdateResultRebuild
UpdateResultExists
UpdateResultNoAction
) )
func NewDocument( func NewDocument(
@ -53,8 +52,54 @@ func NewDocument(
} }
} }
func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, error) { func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) {
return nil, 0, nil var treeChanges []*Change
for _, ch := range changes {
aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch)
if err != nil {
return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err)
}
treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange)
treeChanges = append(treeChanges, treeChange)
err = d.thread.AddChange(ch)
if err != nil {
return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err)
}
}
for _, ch := range treeChanges {
if ch.IsACLChange() {
res, err := d.Build()
return res, UpdateResultRebuild, err
}
}
prevHeads := d.docContext.fullTree.Heads()
mode := d.docContext.fullTree.Add(treeChanges...)
switch mode {
case Nothing:
return d.docContext.docState, UpdateResultNoAction, nil
case Rebuild:
res, err := d.Build()
return res, UpdateResultRebuild, err
default:
break
}
// because for every new change we know it was after any of the previous heads
// each of previous heads must have same "Next" nodes
// so it doesn't matter which one we choose
// so we choose first one
newState, err := d.docStateBuilder.appendFrom(prevHeads[0])
if err != nil {
res, _ := d.Build()
return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded")
}
return newState, UpdateResultAppend, nil
} }
func (d *Document) Build() (DocumentState, error) { func (d *Document) Build() (DocumentState, error) {
@ -77,7 +122,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
} }
if !fromStart { if !fromStart {
d.snapshotValidator.Init(d.docContext.aclTree) err = d.snapshotValidator.Init(d.docContext.aclTree)
if err != nil {
return nil, err
}
valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root) valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -62,3 +62,19 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) {
} }
return s, err return s, err
} }
func (d *documentStateBuilder) appendFrom(fromId string) (s DocumentState, err error) {
d.tree.Iterate(fromId, func(c *Change) (isContinue bool) {
if c.DecryptedDocumentChange != nil {
s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id)
if err != nil {
return false
}
}
return true
})
if err != nil {
return
}
return s, err
}

View File

@ -30,6 +30,26 @@ type ThreadBuilder struct {
keychain *Keychain keychain *Keychain
} }
func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error {
//TODO implement me
panic("implement me")
return nil
}
func (t *ThreadBuilder) MaybeHeads() []string {
//TODO implement me
panic("implement me")
}
func (t *ThreadBuilder) SetMaybeHeads(heads []string) {
//TODO implement me
panic("implement me")
}
func (t *ThreadBuilder) SetHeads(heads []string) {
//TODO implement me
}
func NewThreadBuilder(keychain *Keychain) *ThreadBuilder { func NewThreadBuilder(keychain *Keychain) *ThreadBuilder {
return &ThreadBuilder{ return &ThreadBuilder{
allChanges: make(map[string]*threadChange), allChanges: make(map[string]*threadChange),

View File

@ -2,16 +2,16 @@ package threadmodels
import ( import (
"context" "context"
"github.com/gogo/protobuf/proto"
) )
type Thread interface { type Thread interface {
ID() string ID() string
Heads() []string Heads() []string
MaybeHeads() []string
GetChange(ctx context.Context, recordID string) (*RawChange, error) GetChange(ctx context.Context, recordID string) (*RawChange, error)
//SetHeads(heads []string) SetHeads(heads []string)
//AddChanges(*pb.ACLChange) SetMaybeHeads(heads []string)
PushChange(payload proto.Marshaler) (id string, err error) AddChange(change *RawChange) error
} }
type RawChange struct { type RawChange struct {