Skip to content

Commit

Permalink
feat(logbook): add logbook inspection structs & methods
Browse files Browse the repository at this point in the history
working in flatbuffers brings the burden of needing diagnostic struct methods. Might as well lay the foundations for a logbook subcommand while we're at it

also, fixes a number of bugs, adds a bunch of tests.
  • Loading branch information
b5 committed Oct 12, 2019
1 parent c80d204 commit dd4fb36
Show file tree
Hide file tree
Showing 5 changed files with 634 additions and 115 deletions.
2 changes: 1 addition & 1 deletion actions/dataset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ func TestAddDataset(t *testing.T) {
func TestDataset(t *testing.T) {
rmf := func(t *testing.T) repo.Repo {
store := cafs.NewMapstore()
testPeerProfile.PrivKey = privKey
mr, err := repo.NewMemRepo(testPeerProfile, store, qfs.NewMemFS(), profile.NewMemStore())
if err != nil {
panic(err)
}
// mr.SetPrivateKey(privKey)
return mr
}
DatasetTests(t, rmf)
Expand Down
78 changes: 40 additions & 38 deletions logbook/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ import (
// authors, forming a conflict-free replicated data type (CRDT), and a basis
// for collaboration through knowledge of each other's operations
type Book struct {
authorname string
id string
pk crypto.PrivKey
id string
authorname string
logs map[uint32][]*Log
}

// NewBook initializes a Book
func NewBook(pk crypto.PrivKey, authorname, authorID string) (*Book, error) {
return &Book{
pk: pk,
id: authorID,
authorname: authorname,
logs: map[uint32][]*Log{},
}, nil
Expand All @@ -63,6 +64,11 @@ func (book *Book) AppendLog(l *Log) {
book.logs[l.Model()] = append(book.logs[l.Model()], l)
}

// Logs returns the full map of logs keyed by model type
func (book *Book) Logs() map[uint32][]*Log {
return book.logs
}

// ModelLogs gives all sets whoe model type matches model
func (book *Book) ModelLogs(model uint32) []*Log {
return book.logs[model]
Expand Down Expand Up @@ -164,8 +170,8 @@ func (book Book) marshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOf
func (book *Book) unmarshalFlatbuffer(b *logfb.Book) error {
newBook := Book{
pk: book.pk,
authorname: string(b.Name()),
id: string(b.Identifier()),
authorname: string(b.Name()),
logs: map[uint32][]*Log{},
}

Expand Down Expand Up @@ -195,15 +201,15 @@ func (book Book) logsSlice() (logs []*Log) {
// Log is a causally-ordered set of operations performed by a single author.
// log attribution is verified by an author's signature
type Log struct {
signature []byte
ops []Op
logs []*Log
Signature []byte
Ops []Op
Logs []*Log
}

// InitLog creates a Log from an initialization operation
func InitLog(initop Op) *Log {
return &Log{
ops: []Op{initop},
Ops: []Op{initop},
}
}

Expand All @@ -216,38 +222,33 @@ func FromFlatbufferBytes(data []byte) (*Log, error) {

// Append adds an operation to the log
func (lg *Log) Append(op Op) {
lg.ops = append(lg.ops, op)
}

// Len returns the number of of the latest entry in the log
func (lg Log) Len() int {
return len(lg.ops)
lg.Ops = append(lg.Ops, op)
}

// Model gives the operation type for a log, based on the first operation
// written to the log. Logs can contain multiple models of operations, but the
// first operation written to a log determines the kind of log for
// catagorization purposes
func (lg Log) Model() uint32 {
return lg.ops[0].Model
return lg.Ops[0].Model
}

// Author returns the name and identifier this log is attributed to
func (lg Log) Author() (name, identifier string) {
return lg.ops[0].Name, lg.ops[0].AuthorID
return lg.Ops[0].Name, lg.Ops[0].AuthorID
}

// Name returns the human-readable name for this log, determined by the
// initialization event
// TODO (b5) - name must be made mutable by playing forward any name-changing
// operations and applying them to the log
func (lg Log) Name() string {
return lg.ops[0].Name
return lg.Ops[0].Name
}

// Child returns a child log for a given name, and nil if it doesn't exist
func (lg Log) Child(name string) *Log {
for _, l := range lg.logs {
for _, l := range lg.Logs {
if l.Name() == name {
return l
}
Expand All @@ -257,12 +258,12 @@ func (lg Log) Child(name string) *Log {

// AddChild appends a log as a direct descendant of this log
func (lg *Log) AddChild(l *Log) {
lg.logs = append(lg.logs, l)
lg.Logs = append(lg.Logs, l)
}

// Verify confirms that the signature for a log matches
func (lg Log) Verify(pub crypto.PubKey) error {
ok, err := pub.Verify(lg.SigningBytes(), lg.signature)
ok, err := pub.Verify(lg.SigningBytes(), lg.Signature)
if err != nil {
return err
}
Expand All @@ -272,17 +273,12 @@ func (lg Log) Verify(pub crypto.PubKey) error {
return nil
}

// Ops gives the set of operations in a log
func (lg Log) Ops() []Op {
return lg.ops
}

// Sign assigns the log signature by signing the logging checksum with a given
// private key
// TODO (b5) - this is assuming the log is authored by this private key. as soon
// as we add collaborators, this won't be true
func (lg *Log) Sign(pk crypto.PrivKey) (err error) {
lg.signature, err = pk.Sign(lg.SigningBytes())
lg.Signature, err = pk.Sign(lg.SigningBytes())
if err != nil {
return err
}
Expand All @@ -293,7 +289,7 @@ func (lg *Log) Sign(pk crypto.PrivKey) (err error) {
// SigningBytes perpares a byte slice for signing from a log's operations
func (lg Log) SigningBytes() []byte {
hasher := md5.New()
for _, op := range lg.ops {
for _, op := range lg.Ops {
hasher.Write([]byte(op.Ref))
}
return hasher.Sum(nil)
Expand All @@ -315,9 +311,9 @@ func (lg Log) SignedFlatbufferBytes(pk crypto.PrivKey) ([]byte, error) {
// offset
func (lg Log) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
// build logs bottom up, collecting offsets
logcount := len(lg.logs)
logcount := len(lg.Logs)
logoffsets := make([]flatbuffers.UOffsetT, logcount)
for i, o := range lg.logs {
for i, o := range lg.Logs {
logoffsets[i] = o.MarshalFlatbuffer(builder)
}

Expand All @@ -330,11 +326,11 @@ func (lg Log) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffse
namestr, idstr := lg.Author()
name := builder.CreateString(namestr)
id := builder.CreateString(idstr)
signature := builder.CreateByteString(lg.signature)
signature := builder.CreateByteString(lg.Signature)

count := len(lg.ops)
count := len(lg.Ops)
offsets := make([]flatbuffers.UOffsetT, count)
for i, o := range lg.ops {
for i, o := range lg.Ops {
offsets[i] = o.MarshalFlatbuffer(builder)
}

Expand All @@ -353,29 +349,35 @@ func (lg Log) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffse
return logfb.LogEnd(builder)
}

// UnmarshalFlatbuffer reads a Log from
// UnmarshalFlatbufferBytes is a convenince wrapper to deserialze a flatbuffer
// slice into a log
func (lg *Log) UnmarshalFlatbufferBytes(data []byte) error {
return lg.UnmarshalFlatbuffer(logfb.GetRootAsLog(data, 0))
}

// UnmarshalFlatbuffer populates a logfb.Log from a Log pointer
func (lg *Log) UnmarshalFlatbuffer(lfb *logfb.Log) (err error) {
newLg := Log{}

if len(lfb.Signature()) != 0 {
newLg.signature = lfb.Signature()
newLg.Signature = lfb.Signature()
}

newLg.ops = make([]Op, lfb.OpsetLength())
newLg.Ops = make([]Op, lfb.OpsetLength())
opfb := &logfb.Operation{}
for i := 0; i < lfb.OpsetLength(); i++ {
if lfb.Opset(opfb, i) {
newLg.ops[i] = UnmarshalOpFlatbuffer(opfb)
newLg.Ops[i] = UnmarshalOpFlatbuffer(opfb)
}
}

if lfb.LogsLength() > 0 {
newLg.logs = make([]*Log, lfb.LogsLength())
newLg.Logs = make([]*Log, lfb.LogsLength())
childfb := &logfb.Log{}
for i := 0; i < lfb.LogsLength(); i++ {
if lfb.Logs(childfb, i) {
newLg.logs[i] = &Log{}
newLg.logs[i].UnmarshalFlatbuffer(childfb)
newLg.Logs[i] = &Log{}
newLg.Logs[i].UnmarshalFlatbuffer(childfb)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion logbook/log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestBookFlatbuffer(t *testing.T) {
Size: 2,
Note: "note!",
})
log.signature = []byte{1, 2, 3}
log.Signature = []byte{1, 2, 3}

log.AddChild(InitLog(Op{
Type: OpTypeInit,
Expand Down
Loading

0 comments on commit dd4fb36

Please sign in to comment.