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

View File

@ -14,13 +14,13 @@ type changeLoader struct {
identityKeys map[string]threadmodels.SigningPubKey
signingPubKeyDecoder threadmodels.SigningPubKeyDecoder
thread threadmodels.Thread
changeCreator func(id string, ch *pb.ACLChange) (*Change, error)
changeCreator func(id string, ch *pb.ACLChange) *Change
}
func newChangeLoader(
thread threadmodels.Thread,
signingPubKeyDecoder threadmodels.SigningPubKeyDecoder,
changeCreator func(id string, ch *pb.ACLChange) (*Change, error)) *changeLoader {
changeCreator func(id string, ch *pb.ACLChange) *Change) *changeLoader {
return &changeLoader{
signingPubKeyDecoder: signingPubKeyDecoder,
thread: thread,
@ -48,23 +48,12 @@ func (c *changeLoader) loadChange(id string) (ch *Change, err error) {
return nil, err
}
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)
aclChange, err := c.makeVerifiedACLChange(change)
if err != nil {
return
}
if !verified {
err = fmt.Errorf("the signature of the payload cannot be verified")
return
return nil, err
}
ch, err = c.changeCreator(id, aclChange)
ch = c.changeCreator(id, aclChange)
c.cache[id] = ch
return ch, nil
@ -81,3 +70,22 @@ func (c *changeLoader) verify(identity string, payload, signature []byte) (isVer
}
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
import (
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/pb"
"fmt"
"github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels"
)
@ -28,10 +28,9 @@ type Document struct {
type UpdateResult int
const (
UpdateResultAppend = iota
UpdateResultNoAction = iota
UpdateResultAppend
UpdateResultRebuild
UpdateResultExists
UpdateResultNoAction
)
func NewDocument(
@ -53,8 +52,54 @@ func NewDocument(
}
}
func (d *Document) Update(changes []*pb.ACLChange) (DocumentState, UpdateResult, error) {
return nil, 0, nil
func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) {
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) {
@ -77,7 +122,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
}
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)
if err != nil {
return nil, err

View File

@ -62,3 +62,19 @@ func (d *documentStateBuilder) build() (s DocumentState, err error) {
}
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
}
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 {
return &ThreadBuilder{
allChanges: make(map[string]*threadChange),

View File

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