Skip to content

Commit cfa14be

Browse files
authored
Merge pull request #619 from dedis/backward-compatible-marshalling
Backward compatible marshalling
2 parents 15c4a58 + bbf3d62 commit cfa14be

38 files changed

Lines changed: 3933 additions & 48 deletions

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ linters:
275275
- builtin$
276276
- examples$
277277
- compatible/bigmod/*
278+
- internal/protobuf/*
278279
issues:
279280
max-same-issues: 50
280281
formatters:

encoding_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66

77
"github.com/stretchr/testify/assert"
88
"github.com/stretchr/testify/require"
9-
"go.dedis.ch/protobuf"
9+
"go.dedis.ch/kyber/v4/internal/protobuf"
1010
)
1111

1212
type intWrapper struct {

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ require (
99
github.com/kilic/bls12-381 v0.1.0
1010
github.com/stretchr/testify v1.11.1
1111
go.dedis.ch/fixbuf v1.0.3
12-
go.dedis.ch/protobuf v1.0.11
1312
golang.org/x/crypto v0.44.0
1413
golang.org/x/sys v0.38.0
1514
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZ
55
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
66
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
77
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
8-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
98
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
109
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1110
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
@@ -22,23 +21,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
2221
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2322
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
2423
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
25-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
26-
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
2724
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
2825
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
2926
go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs=
3027
go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw=
31-
go.dedis.ch/kyber/v3 v3.0.4/go.mod h1:OzvaEnPvKlyrWyp3kGXlFdp7ap1VC6RkZDTaPikqhsQ=
32-
go.dedis.ch/kyber/v3 v3.0.9 h1:i0ZbOQocHUjfFasBiUql5zVeC7u/vahFd96DFA8UOWk=
33-
go.dedis.ch/kyber/v3 v3.0.9/go.mod h1:rhNjUUg6ahf8HEg5HUvVBYoWY4boAafX8tYxX+PS+qg=
34-
go.dedis.ch/protobuf v1.0.5/go.mod h1:eIV4wicvi6JK0q/QnfIEGeSFNG0ZeB24kzut5+HaRLo=
35-
go.dedis.ch/protobuf v1.0.7/go.mod h1:pv5ysfkDX/EawiPqcW3ikOxsL5t+BqnV6xHSmE79KI4=
36-
go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo=
37-
go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4=
38-
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
3928
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
4029
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
41-
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
4230
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4331
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
4432
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

internal/protobuf/COPYING

Lines changed: 339 additions & 0 deletions
Large diffs are not rendered by default.

internal/protobuf/README.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
This package is a copy-paste of protobuf v1.0.11 (https://github.com/dedis/protobuf/tree/v1.0.11). The package is
2+
copy-pasted here to remove dependency on the library since it will soon be deprecated. Below is the original README.
3+
4+
# Reflection-based Protocol Buffers
5+
6+
Package protobuf implements Protocol Buffers reflectively using Go types to
7+
define message formats. This approach provides convenience similar to Gob
8+
encoding, but with a widely-used and language-neutral wire format.
9+
For detailed API documentation see https://godoc.org/go.dedis.ch/protobuf.
10+
For general information on Protocol buffers see
11+
https://developers.google.com/protocol-buffers.
12+
13+
## Features
14+
15+
- Reflection-based encoding and decoding to/from protocol buffer wire format.
16+
- Use Go struct field tags to control protobuf fields (ID, optional/required, names).
17+
- Generate `.proto` files from Go structures.
18+
- Encode `time.Time` as an `sfixed64` UnixNano.
19+
- Support for enums.
20+
21+
## Details
22+
23+
In contrast with goprotobuf, this package does not require users to write or
24+
compile .proto files; you just define the message formats you want as Go
25+
struct types. Consider for example this example message format definition from
26+
the Protocol Buffers overview:
27+
28+
```protobuf
29+
message Person {
30+
required string name = 1;
31+
required int32 id = 2;
32+
optional string email = 3;
33+
34+
enum PhoneType {
35+
MOBILE = 0;
36+
HOME = 1;
37+
WORK = 2;
38+
}
39+
40+
message PhoneNumber {
41+
required string number = 1;
42+
optional PhoneType type = 2;
43+
}
44+
45+
repeated PhoneNumber phone = 4;
46+
}
47+
```
48+
49+
The following Go type and const definitions express exactly the same format,
50+
for purposes of encoding and decoding with this protobuf package:
51+
52+
```go
53+
type Person struct {
54+
Name string
55+
Id int32
56+
Email *string
57+
Phone []PhoneNumber
58+
}
59+
60+
type PhoneType uint32
61+
const (
62+
MobilePhone PhoneType = iota
63+
HomePhone
64+
WorkPhone
65+
)
66+
67+
type PhoneNumber struct {
68+
Number string
69+
Type *PhoneType
70+
}
71+
```
72+
73+
To encode a message, you simply call the Encode() function
74+
with a pointer to the struct you wish to encode, and
75+
Encode() returns a []byte slice containing the protobuf-encoded struct:
76+
77+
```go
78+
person := Person{...}
79+
buf := Encode(&person)
80+
output.Write(buf)
81+
```
82+
83+
To decode an encoded message, simply call Decode() on the byte-slice:
84+
85+
```go
86+
err := Decode(buf,&person)
87+
if err != nil {
88+
panic("Decode failed: "+err.Error())
89+
}
90+
```
91+
92+
If you want to interoperate with code in other languages
93+
using the same message formats, you may of course still end up writing
94+
.proto files for the code in those other languages.
95+
96+
However, defining message formats with native Go types enables these types
97+
to be tailored to the code using them without affecting wire compatibility,
98+
such as by attaching useful methods to these struct types.
99+
100+
## Translation Rules
101+
102+
The translation between a Go struct definition and a basic Protocol Buffers
103+
message format definition is straightforward; the rules are as follows.
104+
105+
Go [field tags](https://golang.org/ref/spec#Struct_types) with the key
106+
"protobuf" may be used to control naming, IDs, and optional/required state.
107+
The options are comma-separated like so:
108+
109+
```go
110+
type Tags struct {
111+
Field1 string `protobuf:"10,req,field_1"`
112+
Field2 int32 `protobuf:"20,opt,field_2"`
113+
}
114+
```
115+
116+
A message definition in a .proto file translates to a Go struct, whose fields
117+
are implicitly assigned consecutive numbers starting from 1.
118+
119+
```go
120+
type Padded struct {
121+
Field1 string // = 1
122+
Field2 int32 `protobuf:"3"` // = 3
123+
}
124+
```
125+
126+
A 'required' protobuf field translates to a plain field of a corresponding
127+
type in the Go struct. The following table summarizes the correspondence
128+
between .proto definition types and Go field types:
129+
130+
Protobuf | Go
131+
----------|---------
132+
bool | bool
133+
enum | Enum
134+
int32 | uint32
135+
int64 | uint64
136+
uint32 | uint32
137+
uint64 | uint64
138+
sint32 | int32
139+
sint64 | int64
140+
fixed32 | Ufixed32
141+
fixed64 | Ufixed64
142+
sfixed32 | Sfixed32
143+
sfixed64 | Sfixed64
144+
float | float32
145+
double | float64
146+
string | string
147+
bytes | []byte
148+
message | struct
149+
150+
An 'optional' protobuf field is expressed as a pointer field in Go.
151+
Encode() will transmit the field only if the pointer is non-nil.
152+
Decode() will instantiate the pointed-to type and fill in the pointer
153+
if the field is present in the message being decoded,
154+
leaving the pointer unmodified (usually nil) if the field is not present.
155+
156+
A 'repeated' protobuf field translates to a slice field in Go.
157+
Slices of primitive bool, integer, and float types are encoded
158+
and decoded in packed format, as if the [packed=true] option
159+
was declared for the field in the .proto file.
160+
161+
For flexibility and convenience, struct fields may have interface types,
162+
which this package interprets as having dynamic types to be bound at runtime.
163+
Encode() follows the interface's implicit pointer and uses reflection
164+
to determine the referred-to object's actual type for encoding
165+
Decode() takes an optional map of interface types to constructor functions,
166+
which it uses to instantiate concrete types for interfaces while decoding.
167+
Furthermore, if the instantiated types support the Encoding interface,
168+
Encode() and Decode() will invoke the methods of that interface,
169+
allowing objects to implement their own custom encoding/decoding methods.
170+
171+
This package does not try to support all possible protobuf formats. It
172+
currently does not support nonzero default value declarations for enums, the
173+
legacy unpacked formats for repeated numeric fields, messages with extremely
174+
sparse field numbering, or other more exotic features like extensions or
175+
oneof. If you need to interoperate with existing protobuf code using these
176+
features, then you should probably use goprotobuf, at least for those
177+
particular message formats.
178+
Many of these limitations could be fixed by creative use of
179+
struct tag metadata (see https://golang.org/ref/spec#Struct_types).
180+
181+
Another downside of this reflective approach to protobuf implementation is
182+
that reflective code is generally less efficient than statically generated
183+
code, as gogoprotobuf produces for example. If we decide we want the
184+
convenience of format definitions in Go with the runtime performance of static
185+
code generation, we could in principle achieve that by adding a "Go-format"
186+
message format compiler frontend to goprotobuf or gogoprotobuf - but we leave
187+
this as an exercise for the reader.
188+
189+
190+
## Generating .proto files
191+
192+
`.proto` files can be generated from Go structs using the
193+
`GenerateProtobufDefinition()` function. The following:
194+
195+
```go
196+
types := []interface{}{
197+
Person{},
198+
PhoneNumber{},
199+
}
200+
enums := EnumMap{
201+
"MobilePhone": MobilePhone,
202+
"HomePhone": HomePhone,
203+
"WorkPhone": WorkPhone,
204+
}
205+
GenerateProtobufDefinition(w, types, enums, nil)
206+
```
207+
208+
Will generate:
209+
210+
```protobuf
211+
message Person {
212+
required string name = 1;
213+
required sint32 id = 2;
214+
optional string email = 3;
215+
repeated PhoneNumber phone = 4;
216+
}
217+
218+
message PhoneNumber {
219+
required string number = 1;
220+
optional uint32 type = 2;
221+
}
222+
```
223+
224+
Note: It can be quite tedious to manually synchronise the type and enum maps with the types in your package. I've found [pkgreflect](https://github.com/ungerik/pkgreflect) very useful for automating this.

internal/protobuf/array_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package protobuf
2+
3+
import (
4+
"encoding/hex"
5+
"math"
6+
"reflect"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
type ArrayTest1 struct {
13+
A []int64
14+
}
15+
16+
type ArrayTest2 struct {
17+
A []int32
18+
}
19+
20+
func TestArray(t *testing.T) {
21+
var maxInt32 int32 = math.MaxInt32
22+
var maxInt64 int64 = math.MaxInt64 / 2
23+
24+
a1 := ArrayTest1{[]int64{1, 1, 1}}
25+
a2 := ArrayTest2{[]int32{1, 1, 1}}
26+
a3 := ArrayTest2{[]int32{1, 1, maxInt32}}
27+
a4 := ArrayTest1{[]int64{1, 1, maxInt64}}
28+
29+
buf1, err := Encode(&a1)
30+
require.NoError(t, err)
31+
buf2, err := Encode(&a2)
32+
require.NoError(t, err)
33+
buf3, err := Encode(&a3)
34+
require.NoError(t, err)
35+
buf4, err := Encode(&a4)
36+
require.NoError(t, err)
37+
38+
t.Log(hex.Dump(buf1))
39+
t.Log(hex.Dump(buf2))
40+
t.Log(hex.Dump(buf3))
41+
t.Log(hex.Dump(buf4))
42+
43+
b1 := ArrayTest1{}
44+
b2 := ArrayTest2{}
45+
b3 := ArrayTest2{}
46+
b4 := ArrayTest1{}
47+
48+
err = Decode(buf1, &b1)
49+
require.NoError(t, err)
50+
t.Log(b1, reflect.TypeOf(b1))
51+
52+
err = Decode(buf2, &b2)
53+
require.NoError(t, err)
54+
t.Log(b2, reflect.TypeOf(b2))
55+
56+
err = Decode(buf3, &b3)
57+
require.NoError(t, err)
58+
t.Log(b3, reflect.TypeOf(b3))
59+
60+
err = Decode(buf4, &b4)
61+
require.NoError(t, err)
62+
t.Log(b4, reflect.TypeOf(b4))
63+
64+
require.Equal(t, a1, b1)
65+
require.Equal(t, a2, b2)
66+
require.Equal(t, a3, b3)
67+
require.Equal(t, a4, b4)
68+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package protobuf
2+
3+
import (
4+
"reflect"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestConstructorString(t *testing.T) {
10+
c := &Constructors{
11+
reflect.TypeOf(int64(0)): func() interface{} { return int64(0) },
12+
}
13+
if !strings.HasPrefix(c.String(), "int64=>(func() interface {}") {
14+
t.Fatal("unexpected constructor string: ", c)
15+
}
16+
}

0 commit comments

Comments
 (0)