Add new heads logic

This commit is contained in:
mcrakhman 2022-07-06 23:24:11 +02:00 committed by Mikhail Iudin
parent 0e62047498
commit 2940519b5f
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
11 changed files with 95 additions and 47 deletions

View File

@ -36,7 +36,7 @@ func (tb *ACLTreeBuilder) Init() {
} }
func (tb *ACLTreeBuilder) Build() (*Tree, error) { func (tb *ACLTreeBuilder) Build() (*Tree, error) {
heads := tb.thread.Heads() heads := tb.thread.MaybeHeads()
aclHeads, err := tb.getACLHeads(heads) aclHeads, err := tb.getACLHeads(heads)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -64,6 +64,8 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up
treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange)
treeChanges = append(treeChanges, treeChange) treeChanges = append(treeChanges, treeChange)
// this already sets MaybeHeads to include new changes
// TODO: change this behaviour as non-obvious, because it is not evident from the interface
err = d.thread.AddChange(ch) err = d.thread.AddChange(ch)
if err != nil { if err != nil {
return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err)
@ -99,6 +101,11 @@ func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, Up
return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded")
} }
// setting all heads
d.thread.SetHeads(d.docContext.fullTree.Heads())
// this should be the entrypoint when we build the document
d.thread.SetMaybeHeads(d.docContext.fullTree.Heads())
return newState, UpdateResultAppend, nil return newState, UpdateResultAppend, nil
} }
@ -151,6 +158,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
return nil, err return nil, err
} }
// setting all heads
d.thread.SetHeads(d.docContext.fullTree.Heads())
// this should be the entrypoint when we build the document
d.thread.SetMaybeHeads(d.docContext.fullTree.Heads())
return d.docContext.docState, nil return d.docContext.docState, nil
} }

View File

@ -117,6 +117,6 @@ graph:
baseSnapshot: A.1.2 baseSnapshot: A.1.2
aclHeads: [A.1.2] aclHeads: [A.1.2]
treeHeads: [A.1.2] treeHeads: [A.1.2]
heads: maybeHeads:
- A.1.3 - A.1.3
- B.1.2 - B.1.2

View File

@ -14,11 +14,13 @@ type SymKey struct {
} }
type Keychain struct { type Keychain struct {
SigningKeys map[string]threadmodels.SigningPrivKey SigningKeys map[string]threadmodels.SigningPrivKey
EncryptionKeys map[string]threadmodels.EncryptionPrivKey SigningKeysByIdentity map[string]threadmodels.SigningPrivKey
ReadKeys map[string]*SymKey EncryptionKeys map[string]threadmodels.EncryptionPrivKey
GeneratedIdentities map[string]string ReadKeys map[string]*SymKey
coder *threadmodels.Ed25519SigningPubKeyDecoder ReadKeysByHash map[uint64]*SymKey
GeneratedIdentities map[string]string
coder *threadmodels.Ed25519SigningPubKeyDecoder
} }
func NewKeychain() *Keychain { func NewKeychain() *Keychain {
@ -71,6 +73,7 @@ func (k *Keychain) AddSigningKey(name string) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
k.SigningKeysByIdentity[res] = newPrivKey
k.GeneratedIdentities[name] = res k.GeneratedIdentities[name] = res
} }
@ -87,6 +90,10 @@ func (k *Keychain) AddReadKey(name string) {
Hash: hasher.Sum64(), Hash: hasher.Sum64(),
Key: key, Key: key,
} }
k.ReadKeysByHash[hasher.Sum64()] = &SymKey{
Hash: hasher.Sum64(),
Key: key,
}
} }
func (k *Keychain) AddKey(key string) { func (k *Keychain) AddKey(key string) {

View File

@ -20,36 +20,17 @@ type threadChange struct {
readKey *SymKey readKey *SymKey
signKey threadmodels.SigningPrivKey signKey threadmodels.SigningPrivKey
changesData *pb.PlainTextChangeData changesDataDecrypted []byte
} }
type ThreadBuilder struct { type ThreadBuilder struct {
threadId string threadId string
allChanges map[string]*threadChange allChanges map[string]*threadChange
heads []string heads []string
maybeHeads []string
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),
@ -92,17 +73,58 @@ func (t *ThreadBuilder) Heads() []string {
return t.heads return t.heads
} }
func (t *ThreadBuilder) AddChange(change *threadmodels.RawChange) error {
aclChange := new(pb.ACLChange)
var err error
// 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 fmt.Errorf("could not unmarshall changes")
}
var changesData []byte
// get correct readkey
readKey := t.keychain.ReadKeysByHash[aclChange.CurrentReadKeyHash]
if aclChange.ChangesData != nil {
changesData, err = readKey.Key.Decrypt(aclChange.ChangesData)
if err != nil {
return fmt.Errorf("failed to decrypt changes data: %w", err)
}
}
// get correct signing key
signKey := t.keychain.SigningKeysByIdentity[aclChange.Identity]
t.maybeHeads = append(t.maybeHeads, change.Id)
t.allChanges[change.Id] = &threadChange{
ACLChange: aclChange,
id: change.Id,
readKey: readKey,
signKey: signKey,
changesDataDecrypted: changesData,
}
return nil
}
func (t *ThreadBuilder) MaybeHeads() []string {
return t.maybeHeads
}
func (t *ThreadBuilder) SetMaybeHeads(heads []string) {
// we should copy here instead of just setting the value
t.maybeHeads = heads
}
func (t *ThreadBuilder) SetHeads(heads []string) {
// we should copy here instead of just setting the value
t.heads = heads
}
func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) { func (t *ThreadBuilder) GetChange(ctx context.Context, recordID string) (*threadmodels.RawChange, error) {
rec := t.allChanges[recordID] rec := t.allChanges[recordID]
var encrypted []byte if rec.changesDataDecrypted != nil {
if rec.changesData != nil { encrypted, err := rec.readKey.Key.Encrypt(rec.changesDataDecrypted)
m, err := proto.Marshal(rec.changesData)
if err != nil {
panic("should be able to marshal data!")
}
encrypted, err = rec.readKey.Key.Encrypt(m)
if err != nil { if err != nil {
panic("should be able to encrypt data with read key!") panic("should be able to encrypt data with read key!")
} }
@ -162,9 +184,9 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) {
} }
} }
if len(ch.Changes) > 0 || ch.Snapshot != nil { if len(ch.Changes) > 0 || ch.Snapshot != nil {
newChange.changesData = &pb.PlainTextChangeData{} changesData := &pb.PlainTextChangeData{}
if ch.Snapshot != nil { if ch.Snapshot != nil {
newChange.changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot) changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot)
} }
if len(ch.Changes) > 0 { if len(ch.Changes) > 0 {
var changeContents []*pb.PlainTextChangeContent var changeContents []*pb.PlainTextChangeContent
@ -172,8 +194,13 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) {
aclChangeContent := t.parseDocumentChange(ch) aclChangeContent := t.parseDocumentChange(ch)
changeContents = append(changeContents, aclChangeContent) changeContents = append(changeContents, aclChangeContent)
} }
newChange.changesData.Content = changeContents changesData.Content = changeContents
} }
m, err := proto.Marshal(changesData)
if err != nil {
return
}
newChange.changesDataDecrypted = m
} }
aclChange.CurrentReadKeyHash = k.Hash aclChange.CurrentReadKeyHash = k.Hash
newChange.ACLChange = aclChange newChange.ACLChange = aclChange
@ -421,4 +448,5 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) {
func (t *ThreadBuilder) parseHeads(thread *YMLThread) { func (t *ThreadBuilder) parseHeads(thread *YMLThread) {
t.heads = thread.Heads t.heads = thread.Heads
t.maybeHeads = thread.MaybeHeads
} }

View File

@ -36,13 +36,13 @@ func (t *ThreadBuilder) Graph() (string, error) {
style := "solid" style := "solid"
if r.GetAclData() != nil { if r.GetAclData() != nil {
style = "filled" style = "filled"
} else if r.changesData != nil { } else if r.changesDataDecrypted != nil {
style = "dashed" style = "dashed"
} }
var chSymbs []string var chSymbs []string
if r.changesData != nil { if r.changesDataDecrypted != nil {
for _, chc := range r.changesData.Content { for _, chc := range r.changesDataDecrypted.Content {
tp := fmt.Sprintf("%T", chc.Value) tp := fmt.Sprintf("%T", chc.Value)
tp = strings.Replace(tp, "ChangeContentValueOf", "", 1) tp = strings.Replace(tp, "ChangeContentValueOf", "", 1)
res := "" res := ""

View File

@ -99,5 +99,5 @@ graph:
baseSnapshot: A.1.1 baseSnapshot: A.1.1
aclHeads: [B.1.1] aclHeads: [B.1.1]
treeHeads: [B.1.1] treeHeads: [B.1.1]
heads: maybeHeads:
- "A.1.3" - "A.1.3"

View File

@ -101,6 +101,6 @@ graph:
baseSnapshot: A.1.1 baseSnapshot: A.1.1
aclHeads: [A.1.2] aclHeads: [A.1.2]
treeHeads: [A.1.2] treeHeads: [A.1.2]
heads: maybeHeads:
- "A.1.3" - "A.1.3"
- "B.1.2" - "B.1.2"

View File

@ -101,6 +101,6 @@ graph:
aclSnapshot: A.1.1 aclSnapshot: A.1.1
aclHeads: [A.1.3] aclHeads: [A.1.3]
treeHeads: [A.1.3] treeHeads: [A.1.3]
heads: maybeHeads:
- "A.1.4" - "A.1.4"
- "B.1.2" - "B.1.2"

View File

@ -124,7 +124,7 @@ graph:
baseSnapshot: A.1.2 baseSnapshot: A.1.2
aclHeads: [A.1.2] aclHeads: [A.1.2]
treeHeads: [A.1.2] treeHeads: [A.1.2]
heads: maybeHeads:
- "A.1.3" - "A.1.3"
- "B.1.2" - "B.1.2"

View File

@ -94,5 +94,6 @@ type YMLThread struct {
TreeHeads []string `yaml:"treeHeads"` TreeHeads []string `yaml:"treeHeads"`
} `yaml:"graph"` } `yaml:"graph"`
Heads []string `yaml:"heads"` Heads []string `yaml:"heads"`
MaybeHeads []string `yaml:"maybeHeads"`
} }