Skip to content

Commit

Permalink
fix(prove): Prove command updates local logbook as needed
Browse files Browse the repository at this point in the history
  • Loading branch information
dustmop committed Feb 16, 2021
1 parent 384fdb2 commit b09a2eb
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 8 deletions.
44 changes: 43 additions & 1 deletion lib/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"context"
"encoding/base64"
"fmt"
"path/filepath"

"github.com/qri-io/qri/base"
"github.com/qri-io/qri/config"
"github.com/qri-io/qri/logbook"
"github.com/qri-io/qri/logbook/oplog"
"github.com/qri-io/qri/profile"
"github.com/qri-io/qri/registry"
)
Expand Down Expand Up @@ -58,6 +61,19 @@ func (m RegistryClientMethods) ProveProfileKey(p *RegistryProfile, ok *bool) err

ctx := context.Background()

// Check if the repository has any saved datasets. If so, calling prove is
// not allowed, because doing so would essentially throw away the old profile,
// making those references unreachable. In the future, this can be changed
// such that the old identity is given a different username, and is merged
// into the client's collection.
numRefs, err := m.inst.repo.RefCount()
if err != nil {
return err
}
if numRefs > 0 {
return fmt.Errorf("cannot prove with a non-empty repository")
}

// For signing the outgoing message
// TODO(arqu): this should take the profile PK instead of active PK once multi tenancy is supported
privKey := m.inst.repo.PrivateKey(ctx)
Expand Down Expand Up @@ -92,7 +108,33 @@ func (m RegistryClientMethods) ProveProfileKey(p *RegistryProfile, ok *bool) err
} else {
return fmt.Errorf("prove: server response invalid, did not have profileID")
}
// TODO(dustmop): Also get logbook

// If an existing user of this profileID pushed something, get the previous logbook data
// and write it to our repository. This enables push and pull to continue to work
if logbookBin, ok := res["logbookInit"]; ok && len(logbookBin) > 0 {
logbookBytes, err := base64.StdEncoding.DecodeString(logbookBin)
if err != nil {
return err
}
lg := &oplog.Log{}
if err := lg.UnmarshalFlatbufferBytes(logbookBytes); err != nil {
return err
}
err = m.inst.repo.Logbook().ReplaceAll(ctx, lg)
if err != nil {
return err
}
} else {
// Otherwise, nothing was ever pushed. Create new logbook data using the
// profileID we got back.
logbookPath := filepath.Join(m.inst.repoPath, "logbook.qfb")
logbook, err := logbook.NewJournalOverwriteWithProfileID(privKey, p.Username, m.inst.bus,
m.inst.qfs, logbookPath, cfg.Profile.ID)
if err != nil {
return err
}
m.inst.logbook = logbook
}

// Save the modified config
return m.inst.ChangeConfig(cfg)
Expand Down
56 changes: 49 additions & 7 deletions logbook/logbook.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ func NewJournal(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesy

if err := book.load(ctx); err != nil {
if err == ErrNotFound {
err = book.initialize(ctx)
keyID, err := profile.KeyIDFromPriv(book.pk)
if err != nil {
return nil, err
}
err = book.initialize(ctx, keyID)
return book, err
}
return nil, err
Expand All @@ -145,23 +149,51 @@ func NewJournal(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesy
return book, nil
}

func (book *Book) initialize(ctx context.Context) error {
keyID, err := profile.KeyIDFromPriv(book.pk)
if err != nil {
return err
// NewJournalOverwriteWithProfileID initializes a new logbook using the
// given profileID. Any existing logbook will be overwritten.
func NewJournalOverwriteWithProfileID(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesystem, location, profileID string) (*Book, error) {
ctx := context.Background()
if pk == nil {
return nil, fmt.Errorf("logbook: private key is required")
}
if fs == nil {
return nil, fmt.Errorf("logbook: filesystem is required")
}
if location == "" {
return nil, fmt.Errorf("logbook: location is required")
}
if profileID == "" {
return nil, fmt.Errorf("logbook: profileID is required")
}
if bus == nil {
return nil, fmt.Errorf("logbook: event.Bus is required")
}

book := &Book{
store: &oplog.Journal{},
fs: fs,
pk: pk,
authorName: username,
fsLocation: location,
publisher: bus,
}

err := book.initialize(ctx, profileID)
return book, err
}

func (book *Book) initialize(ctx context.Context, authorID string) error {
// initialize author's log of user actions
userActions := oplog.InitLog(oplog.Op{
Type: oplog.OpTypeInit,
Model: AuthorModel,
Name: book.Username(),
AuthorID: keyID,
AuthorID: authorID,
Timestamp: NewTimestamp(),
})
book.authorID = userActions.ID()

if err = book.store.MergeLog(ctx, userActions); err != nil {
if err := book.store.MergeLog(ctx, userActions); err != nil {
return err
}
if al, ok := book.store.(oplog.AuthorLogstore); ok {
Expand Down Expand Up @@ -215,6 +247,16 @@ func (book *Book) DeleteAuthor() error {
return fmt.Errorf("not finished")
}

// ReplaceAll replaces the contents of the logbook with
// the provided log data
func (book *Book) ReplaceAll(ctx context.Context, lg *oplog.Log) error {
err := book.store.ReplaceAll(ctx, lg)
if err != nil {
return err
}
return book.save(ctx)
}

// save writes the book to book.fsLocation
func (book *Book) save(ctx context.Context) (err error) {
if al, ok := book.store.(oplog.AuthorLogstore); ok {
Expand Down
10 changes: 10 additions & 0 deletions logbook/oplog/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ type Logstore interface {
// get all generations of a log, using the given log as an outparam
// Descendants MUST only mutate Logs field of the passed-in log pointer
Descendants(ctx context.Context, l *Log) error

// ReplaceAll replaces the contents of the entire log
ReplaceAll(ctx context.Context, l *Log) error
}

// SparseAncestorsAllDescendantsLogstore is a an extension interface to
Expand Down Expand Up @@ -335,6 +338,13 @@ func (j *Journal) Descendants(ctx context.Context, l *Log) error {
return nil
}

// ReplaceAll replaces the entirety of the logs
func (j *Journal) ReplaceAll(ctx context.Context, l *Log) error {
j.logs = []*Log{l}
j.id = l.Ops[0].Hash()
return nil
}

// FlatbufferCipher marshals journal to a flatbuffer and encrypts the book using
// a given private key. This same private key must be retained elsewhere to read
// the flatbuffer later on
Expand Down

0 comments on commit b09a2eb

Please sign in to comment.