Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgradability protocol boilerplate #10

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["appulate.filewatcher"]
}
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
{
"filewatcher.commands": [
{
"cmd": "cd ${fileDirname}/../..; go run vsc-node/scripts/manager_gen",
"event": "onFileChange",
"isAsync": true,
"match": "/modules/protocol/protocol.go"
}
]
// "go.testTimeout": "300s"
// "go.testFlags": ["-count=1"] // disable test cache
}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# go-vsc-node

Golang Version of VSC

## Development

This project has some code generation.

Running Manually:
`go run vsc-node/scripts/manager_gen` (from project root directory)

Automatic Updates:
Install the [File Watcher](https://marketplace.visualstudio.com/items?itemName=Appulate.filewatcher) VSCode extension.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,13 @@ require (

require (
github.com/FerretDB/FerretDB v1.24.0
github.com/JustinKnueppel/go-result v1.0.0
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/dave/jennifer v1.7.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ github.com/FerretDB/FerretDB v1.24.0 h1:7WJmezL48Bj9bYWnhT/bEJgX5gjT5s7LFdHTqkN2
github.com/FerretDB/FerretDB v1.24.0/go.mod h1:E7e8dVcgsQim1k9jQ5LmP0HDQ3beZ1s1UnE3BsyerLw=
github.com/FerretDB/wire v0.0.7 h1:ZDsz3CgNjJ7vkr9ZDcqpcu0lj298GuhVA9ZrIqT8tD8=
github.com/FerretDB/wire v0.0.7/go.mod h1:2HkyhNgxvEOZotjeZP4dVDgZ3aUcYFilL/tXLrHXZmI=
github.com/JustinKnueppel/go-result v1.0.0 h1:YS9tHkkX/PtNgEucR9aA4g2p6S7Kr+1V6tniM/U0Jqk=
github.com/JustinKnueppel/go-result v1.0.0/go.mod h1:rzEtGC5roEDDi4P0dDCpvXuKcgGJiHtqhPJNlPGEq38=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/SAP/go-hdb v1.10.1 h1:c9dGT5xHZNDwPL3NQcRpnNISn3MchwYaGoMZpCAllUs=
Expand Down Expand Up @@ -76,6 +78,8 @@ github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668 h1:ZFUue+PN
github.com/crackcomm/go-gitignore v0.0.0-20231225121904-e25f5bc08668/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE=
github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0=
github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
21 changes: 21 additions & 0 deletions modules/manager/manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package manager

import (
"vsc-node/modules/protocol"
)

type manager struct {
protocols []protocol.Protocol
version uint
}

var Manager = manager{
protocols: []protocol.Protocol{
protocol.V1,
protocol.V2,
},
}

func (m *manager) SetVersion(version uint) {
m.version = version
}
30 changes: 30 additions & 0 deletions modules/manager/manager_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
This is an auto-generated file. DO NOT EDIT!
To modify generation, update /scripts/manager_gen/manager_gen.go
To modify protocol interface, update /modules/protocol/protocol.go
*/

package manager

import result "github.com/JustinKnueppel/go-result"

func (m *manager) ProposeBlock(id string) result.Result[any] {
for i := m.version; i >= 0; i-- {
protocol := m.protocols[i]
if protocol.ProposeBlock != nil {
return protocol.ProposeBlock(id)
}
}
var res result.Result[any]
return res
}
func (m *manager) SignMultiSig() {
for i := m.version; i >= 0; i-- {
protocol := m.protocols[i]
if protocol.SignMultiSig != nil {
protocol.SignMultiSig()
return
}
}
return
}
8 changes: 8 additions & 0 deletions modules/protocol/protocol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package protocol

import "github.com/JustinKnueppel/go-result"

type Protocol struct {
ProposeBlock func(id string) (res result.Result[any])
SignMultiSig func()
}
5 changes: 5 additions & 0 deletions modules/protocol/v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package protocol

var V1 = Protocol{
SignMultiSig: func() {},
}
5 changes: 5 additions & 0 deletions modules/protocol/v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package protocol

var V2 = Protocol{
SignMultiSig: func() {},
}
220 changes: 220 additions & 0 deletions scripts/manager_gen/manager_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package main

import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"

. "github.com/dave/jennifer/jen"
)

var knownImports = map[string]string{
"result": "github.com/JustinKnueppel/go-result",
}

const InputFilePath = "./modules/protocol/protocol.go"
const InputStructName = "Protocol"

const OutputFilePath = "./modules/manager/manager_gen.go"
const OutputStructName = "manager"

func main() {
fset := token.NewFileSet() // positions are relative to fset

f := NewFile("manager")

f.HeaderComment("This is an auto-generated file. DO NOT EDIT!\n" +
"To modify generation, update /scripts/manager_gen/manager_gen.go\n" +
"To modify protocol interface, update /modules/protocol/protocol.go")

for importName, importPath := range knownImports {
f.ImportAlias(importPath, importName)
}

// Parse src but stop after processing the imports.
fp, err := parser.ParseFile(fset, InputFilePath, nil, parser.AllErrors)
if err != nil {
fmt.Println(err)
return
}

done := false

for _, dec := range fp.Decls {
if done {
break
}
dec, ok := dec.(*ast.GenDecl)
if !ok {
continue
}
if dec.Tok != token.TYPE {
continue
}
// fmt.Printf("declaration: %+v\n", dec.Tok)
for _, spec := range dec.Specs {
if done {
break
}
spec, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}
name := spec.Name.Name
if name != InputStructName {
continue
}
// typeParams := spec.TypeParams
// fmt.Printf(" spec: %+v\n", spec.Type)
Type, ok := spec.Type.(*ast.StructType)
if !ok {
continue
}
if Type.Incomplete {
fmt.Println("syntax error")
return
}
for _, field := range Type.Fields.List {
// fmt.Printf(" field: %+v\n", field)
// fmt.Printf(" type: %+v\n", field.Type)
Type, ok := field.Type.(*ast.FuncType)
if !ok {
continue
}
var results []*ast.Field
if Type.Results != nil {
results = Type.Results.List
}
fnCall := Id("protocol").Dot(field.Names[0].Name).Call(GenerateCallArgs(Type.Params.List)...)
execBlock := []Code{Return(fnCall)}
if len(results) == 0 {
execBlock = []Code{fnCall, Return()}
}
WithReturnType(f.Func().Params(
Id("m").Id("*"+OutputStructName),
).Id(field.Names[0].Name).Params(
GenerateParams(Type.Params.List)...,
), results).Block(
append([]Code{
For(
Id("i").Op(":=").Id("m").Dot("version"),
Id("i").Op(">=").Lit(0),
Id("i").Op("--"),
).Block(
Id("protocol").Op(":=").Id("m").Dot("protocols").Index(Id("i")),
If(Id("protocol").Dot(field.Names[0].Name).Op("!=").Id("nil")).Block(
execBlock...,
),
),
},
append(
GenerateDefaultValues(results),
Return(GenerateDefaultReturnValue(results)...),
)...,
)...,
)
}
done = true
break
}
}

file, err := os.Create(OutputFilePath)
if err != nil {
fmt.Println("err:", err)
return
}

fmt.Fprintf(file, "%#v", f)
}

func GenerateDefaultReturnValue(fields []*ast.Field) (res []Code) {
for i, field := range fields {
if len(field.Names) > 0 {
res = append(res, Id(field.Names[0].Name))
} else {
res = append(res, Id(fmt.Sprintf("res%d", i)))
}
}
return
}

func GenerateDefaultValues(fields []*ast.Field) (res []Code) {
for i, field := range fields {
var stmt *Statement
if len(field.Names) > 0 {
stmt = Var().Id(field.Names[0].Name)
} else {
stmt = Var().Id(fmt.Sprintf("res%d", i))
}
stmt.Add(generateType(field.Type))
res = append(res, stmt)
}
return
}

func generateType(expr ast.Expr) *Statement {
switch Type := expr.(type) {
case *ast.Ident:
return Id(Type.Name)
case *ast.IndexExpr:
return generateType(Type.X).Index(generateType(Type.Index))
case *ast.IndexListExpr:
params := make([]Code, len(Type.Indices))
for i, indexType := range Type.Indices {
params[i] = generateType((indexType))
}
return generateType(Type.X).Index(params...)
case *ast.SelectorExpr:
id, ok := Type.X.(*ast.Ident)
if !ok {
return generateType(Type.X).Dot(Type.Sel.Name)
}
name, ok := knownImports[id.Name]
if !ok {
name = id.Name
}
return Qual(name, Type.Sel.Name)
default:
fmt.Printf("type not supported %+v\n", expr)
return new(Statement)
}
}

func WithReturnType(s *Statement, fields []*ast.Field) *Statement {
if len(fields) == 0 {
return s
}
var res []Code
for _, field := range fields {
res = append(res, generateType(field.Type))
}
return s.Params(res...)
}

func GenerateParams(fields []*ast.Field) (res []Code) {
for i, field := range fields {
var stmt *Statement
if len(field.Names) > 0 {
stmt = Id(field.Names[0].Name)
} else {
stmt = Id(fmt.Sprintf("arg%d", i))
}
stmt.Add(generateType(field.Type))
res = append(res, stmt)
}
return
}

func GenerateCallArgs(fields []*ast.Field) (res []Code) {
for i, field := range fields {
if len(field.Names) > 0 {
res = append(res, Id(field.Names[0].Name))
} else {
res = append(res, Id(fmt.Sprintf("arg%d", i)))
}
}
return
}