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) {
heads := tb.thread.Heads()
heads := tb.thread.MaybeHeads()
aclHeads, err := tb.getACLHeads(heads)
if err != nil {
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)
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)
if err != nil {
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")
}
// 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
}
@ -151,6 +158,11 @@ func (d *Document) build(fromStart bool) (DocumentState, error) {
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
}

View File

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

View File

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

View File

@ -20,36 +20,17 @@ type threadChange struct {
readKey *SymKey
signKey threadmodels.SigningPrivKey
changesData *pb.PlainTextChangeData
changesDataDecrypted []byte
}
type ThreadBuilder struct {
threadId string
allChanges map[string]*threadChange
heads []string
maybeHeads []string
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),
@ -92,17 +73,58 @@ func (t *ThreadBuilder) Heads() []string {
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) {
rec := t.allChanges[recordID]
var encrypted []byte
if rec.changesData != nil {
m, err := proto.Marshal(rec.changesData)
if err != nil {
panic("should be able to marshal data!")
}
encrypted, err = rec.readKey.Key.Encrypt(m)
if rec.changesDataDecrypted != nil {
encrypted, err := rec.readKey.Key.Encrypt(rec.changesDataDecrypted)
if err != nil {
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 {
newChange.changesData = &pb.PlainTextChangeData{}
changesData := &pb.PlainTextChangeData{}
if ch.Snapshot != nil {
newChange.changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot)
changesData.Snapshot = t.parseChangeSnapshot(ch.Snapshot)
}
if len(ch.Changes) > 0 {
var changeContents []*pb.PlainTextChangeContent
@ -172,8 +194,13 @@ func (t *ThreadBuilder) Parse(thread *YMLThread) {
aclChangeContent := t.parseDocumentChange(ch)
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
newChange.ACLChange = aclChange
@ -421,4 +448,5 @@ func (t *ThreadBuilder) parseGraph(thread *YMLThread) {
func (t *ThreadBuilder) parseHeads(thread *YMLThread) {
t.heads = thread.Heads
t.maybeHeads = thread.MaybeHeads
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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