Skip to content

Commit

Permalink
feat(log): flatbuffer encoding code & test work
Browse files Browse the repository at this point in the history
  • Loading branch information
b5 committed Sep 22, 2019
1 parent 4545d66 commit b88d899
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 95 deletions.
2 changes: 1 addition & 1 deletion log/log.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ table Book {
name:string; // book author name
identifier:string; // book author identifier
authors:[Logset]; // list of author keys
names:[Logset]; // named dataset logs
datasets:[Logset]; // named dataset logs
}

root_type Book;
231 changes: 159 additions & 72 deletions log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import (
// for collaboration through knowledge of each other's operations
type Book struct {
username string
id string
pk crypto.PrivKey
authors []logset
logs []logset
authors []*logset
datasets []*logset
}

// NewBook initializes a logbook, reading any existing data at the given
Expand Down Expand Up @@ -55,16 +56,16 @@ func (book Book) NameInit(name string) error {
timestamp: now,
},
}
l := log{
l := &log{
ops: []operation{o},
}
set := logset{
set := &logset{
root: name,
logs: map[string]log{
logs: map[string]*log{
name: l,
},
}
book.logs = append(book.logs, set)
book.datasets = append(book.datasets, set)
return fmt.Errorf("not finished")
}

Expand Down Expand Up @@ -152,62 +153,73 @@ func (book Book) flatbufferBytes() []byte {
}

func (book Book) marshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
// TODO (b5) - finish
return 0
}

func (book *Book) unmarshalFlatbuffer(b *logfb.Book) error {
// TODO (b5) - finish
return fmt.Errorf("not finished")
}

// 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 []operation
}
username := builder.CreateString(book.username)
id := builder.CreateString(book.id)

// Len returns the number of of the latest entry in the log
func (set log) Len() int {
return len(set.ops)
}
count := len(book.authors)
offsets := make([]flatbuffers.UOffsetT, count)
for i, lset := range book.authors {
offsets[i] = lset.MarshalFlatbuffer(builder)
}
logfb.BookStartAuthorsVector(builder, count)
for i := count - 1; i >= 0; i-- {
builder.PrependUOffsetT(offsets[i])
}
authors := builder.EndVector(count)

func (set log) Type() string {
return ""
}
count = len(book.datasets)
offsets = make([]flatbuffers.UOffsetT, count)
for i, lset := range book.datasets {
offsets[i] = lset.MarshalFlatbuffer(builder)
}
logfb.BookStartAuthorsVector(builder, count)
for i := count - 1; i >= 0; i-- {
builder.PrependUOffsetT(offsets[i])
}
datasets := builder.EndVector(count)

func (set log) Author() (name, identifier string) {
// TODO (b5) - name and identifier must come from init operation
return "", ""
logfb.BookStart(builder)
logfb.BookAddName(builder, username)
logfb.BookAddIdentifier(builder, id)
logfb.BookAddAuthors(builder, authors)
logfb.BookAddDatasets(builder, datasets)
return logfb.BookEnd(builder)
}

func (set log) Verify() error {
return fmt.Errorf("not finished")
}
func (book *Book) unmarshalFlatbuffer(b *logfb.Book) error {
newBook := Book{}

newBook.authors = make([]*logset, b.AuthorsLength())
var logsetfb logfb.Logset
for i := 0; i < b.AuthorsLength(); i++ {
if b.Authors(&logsetfb, i) {
newBook.authors[i] = &logset{}
if err := newBook.authors[i].UnmarshalFlatbuffer(&logsetfb); err != nil {
return err
}
}
}

func (set log) MarshalFlatbuffer(builder *flatbuffers.Builder, name, identifier string) flatbuffers.UOffsetT {
// count := len(set)
// offsets := make([]flatbuffers.UOffsetT, count)
// for i, o := range set {
// offsets[i] = o.MarshalFlatbuffer(builder)
// }
newBook.datasets = make([]*logset, b.DatasetsLength())
for i := 0; i < b.DatasetsLength(); i++ {
if ok := b.Datasets(&logsetfb, i); ok {
newBook.datasets[i] = &logset{}
if err := newBook.datasets[i].UnmarshalFlatbuffer(&logsetfb); err != nil {
return err
}
}
}

// logfb.OpStartListVector(builder, count)
// for i := count - 1; i >= 0; i-- {
// builder.PrependUOffsetT(offsets[i])
// }
// return builder.EndVector(count)
// TODO (b5)
return 0
*book = newBook
return nil
}

// logset is a collection of unique logs
type logset struct {
signer string
signature []byte
root string
logs map[string]log
logs map[string]*log
}

func (ls logset) Author() (string, string) {
Expand All @@ -220,56 +232,131 @@ func (ls logset) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOf
namestr, idstr := ls.Author()
name := builder.CreateString(namestr)
id := builder.CreateString(idstr)
root := builder.CreateString(ls.root)

count := len(ls.logs)
offsets := make([]flatbuffers.UOffsetT, count)
i := 0
for _, log := range ls.logs {
name, id := log.Author()
offsets[i] = log.MarshalFlatbuffer(builder, name, id)
offsets[i] = log.MarshalFlatbuffer(builder)
i++
}

logfb.LogStartOpsetVector(builder, count)
logfb.LogsetStartLogsVector(builder, count)
for i := count - 1; i >= 0; i-- {
builder.PrependUOffsetT(offsets[i])
}
ops := builder.EndVector(count)
logs := builder.EndVector(count)

logfb.LogStart(builder)
logfb.LogAddName(builder, name)
logfb.LogAddIdentifier(builder, id)
logfb.LogAddOpset(builder, ops)
logfb.LogsetStart(builder)
logfb.LogsetAddName(builder, name)
logfb.LogsetAddRoot(builder, root)
logfb.LogsetAddIdentifier(builder, id)
logfb.LogsetAddLogs(builder, logs)
return logfb.LogEnd(builder)
}

func (ls logset) UnmarshalFlatbuffer(flatlog *logfb.Log) error {
func (ls *logset) UnmarshalFlatbuffer(lsfb *logfb.Logset) (err error) {
newLs := logset{
root: string(lsfb.Root()),
logs: map[string]*log{},
}

lgfb := &logfb.Log{}
for i := 0; i < lsfb.LogsLength(); i++ {
if lsfb.Logs(lgfb, i) {
lg := &log{}
if err = lg.UnmarshalFlatbuffer(lgfb); err != nil {
return err
}
newLs.logs[lg.Name()] = lg
}
}

*ls = newLs
return nil
}

type logmap map[string]logset
// 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 []operation
}

// Len returns the number of of the latest entry in the log
func (lg log) Len() int {
return len(lg.ops)
}

func (lg log) Type() string {
return ""
}

func (lg log) Author() (name, identifier string) {
// TODO (b5) - name and identifier must come from init operation
if len(lg.ops) > 0 {
if initOp, ok := lg.ops[0].(initOperation); ok {
return initOp.AuthorName(), initOp.AuthorID()
}
}
return "", ""
}

func (lg log) Name() string {
if len(lg.ops) > 0 {
if initOp, ok := lg.ops[0].(initOperation); ok {
return initOp.Name()
}
}
return ""
}

func (lg log) Verify() error {
return fmt.Errorf("not finished")
}

func (lg log) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
namestr, idstr := lg.Author()
name := builder.CreateString(namestr)
id := builder.CreateString(idstr)
signature := builder.CreateByteString(lg.signature)

func (l logmap) MarshalFlatbuffer(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
count := len(l)
count := len(lg.ops)
offsets := make([]flatbuffers.UOffsetT, count)
i := 0
for _, lg := range l {
offsets[i] = lg.MarshalFlatbuffer(builder)
i++
for i, o := range lg.ops {
offsets[i] = o.MarshalFlatbuffer(builder)
}

logfb.LogsetStartLogsVector(builder, count)
logfb.LogStartOpsetVector(builder, count)
for i := count - 1; i >= 0; i-- {
builder.PrependUOffsetT(offsets[i])
}
logs := builder.EndVector(count)
ops := builder.EndVector(count)

logfb.LogsetStart(builder)
logfb.LogsetAddLogs(builder, logs)
return logfb.LogsetEnd(builder)
return 0
logfb.LogStart(builder)
logfb.LogAddName(builder, name)
logfb.LogAddIdentifier(builder, id)
logfb.LogAddSignature(builder, signature)
logfb.LogAddOpset(builder, ops)
return logfb.LogEnd(builder)
}

func (l logmap) UnmarshalFlatbuffer(logset *logfb.Logset) error {
return fmt.Errorf("not finished")
func (lg *log) UnmarshalFlatbuffer(lfb *logfb.Log) (err error) {
newLg := log{
signature: lfb.Signature(),
}

newLg.ops = make([]operation, lfb.OpsetLength())
opfb := &logfb.Operation{}
for i := 0; i < lfb.OpsetLength(); i++ {
if lfb.Opset(opfb, i) {
if newLg.ops[i], err = unmarshalOperationFlatbuffer(opfb); err != nil {
return err
}
}
}

*lg = newLg
return nil
}
18 changes: 9 additions & 9 deletions log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,28 +176,28 @@ func TestNewBook(t *testing.T) {
}

func TestBookFlatbuffer(t *testing.T) {
everyOpLog := log{
everyOpLog := &log{
signature: nil,
ops: []operation{
userInit{
op: op{
opType: opTypeUserInit,
ref: "QmHashOfSteveSPublicKey",
},
Author: "steve",
Username: "steve",
},
},
}

set := logset{
logs: map[string]log{
"branch": everyOpLog,
set := &logset{
logs: map[string]*log{
"steve": everyOpLog,
},
}

book := Book{
authors: []logset{set},
logs: []logset{set},
book := &Book{
authors: []*logset{set},
datasets: []*logset{set},
}

data := book.flatbufferBytes()
Expand All @@ -208,7 +208,7 @@ func TestBookFlatbuffer(t *testing.T) {
t.Fatalf("unmarshalling flatbuffer bytes: %s", err.Error())
}

if diff := cmp.Diff(book, got); diff != "" {
if diff := cmp.Diff(book, got, cmp.AllowUnexported(Book{}, logset{}, log{}, userInit{}, op{})); diff != "" {
t.Errorf("result mismatch (-want +got):\n%s", diff)
}
}
10 changes: 5 additions & 5 deletions log/logfb/Book.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b88d899

Please sign in to comment.