Ver código fonte

lib/protocol: Use DeviceID in protocol messages, with custom marshalling

This makes the device ID a real type that can be used in the protobuf
schema. That avoids the juggling back and forth from []byte in a bunch
of places and simplifies the code.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3695
Jakob Borg 9 anos atrás
pai
commit
0296c23685

+ 7 - 8
cmd/stdisco/main.go

@@ -7,7 +7,6 @@
 package main
 
 import (
-	"bytes"
 	"crypto/rand"
 	"encoding/binary"
 	"flag"
@@ -45,7 +44,7 @@ func main() {
 	flag.Parse()
 
 	if fake {
-		log.Println("My ID:", protocol.DeviceIDFromBytes(myID))
+		log.Println("My ID:", myID)
 	}
 
 	runbeacon(beacon.NewMulticast(mc), fake)
@@ -75,17 +74,17 @@ func recv(bc beacon.Interface) {
 		var ann discover.Announce
 		ann.Unmarshal(data[4:])
 
-		if bytes.Equal(ann.ID, myID) {
+		if ann.ID == myID {
 			// This is one of our own fake packets, don't print it.
 			continue
 		}
 
 		// Print announcement details for the first packet from a given
 		// device ID and source address, or if -all was given.
-		key := string(ann.ID) + src.String()
+		key := ann.ID.String() + src.String()
 		if all || !seen[key] {
 			log.Printf("Announcement from %v\n", src)
-			log.Printf(" %v at %s\n", protocol.DeviceIDFromBytes(ann.ID), strings.Join(ann.Addresses, ", "))
+			log.Printf(" %v at %s\n", ann.ID, strings.Join(ann.Addresses, ", "))
 			seen[key] = true
 		}
 	}
@@ -106,9 +105,9 @@ func send(bc beacon.Interface) {
 }
 
 // returns a random but recognizable device ID
-func randomDeviceID() []byte {
-	var id [32]byte
+func randomDeviceID() protocol.DeviceID {
+	var id protocol.DeviceID
 	copy(id[:], randomPrefix)
 	rand.Read(id[len(randomPrefix):])
-	return id[:]
+	return id
 }

+ 7 - 11
lib/discover/local.go

@@ -10,7 +10,6 @@
 package discover
 
 import (
-	"bytes"
 	"encoding/binary"
 	"encoding/hex"
 	"io"
@@ -115,7 +114,7 @@ func (c *localClient) Error() error {
 
 func (c *localClient) announcementPkt() Announce {
 	return Announce{
-		ID:         c.myID[:],
+		ID:         c.myID,
 		Addresses:  c.addrList.AllAddresses(),
 		InstanceID: rand.Int63(),
 	}
@@ -173,10 +172,10 @@ func (c *localClient) recvAnnouncements(b beacon.Interface) {
 			continue
 		}
 
-		l.Debugf("discover: Received local announcement from %s for %s", addr, protocol.DeviceIDFromBytes(pkt.ID))
+		l.Debugf("discover: Received local announcement from %s for %s", addr, pkt.ID)
 
 		var newDevice bool
-		if !bytes.Equal(pkt.ID, c.myID[:]) {
+		if pkt.ID != c.myID {
 			newDevice = c.registerDevice(addr, pkt)
 		}
 
@@ -192,20 +191,17 @@ func (c *localClient) recvAnnouncements(b beacon.Interface) {
 }
 
 func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
-	var id protocol.DeviceID
-	copy(id[:], device.ID)
-
 	// Remember whether we already had a valid cache entry for this device.
 	// If the instance ID has changed the remote device has restarted since
 	// we last heard from it, so we should treat it as a new device.
 
-	ce, existsAlready := c.Get(id)
+	ce, existsAlready := c.Get(device.ID)
 	isNewDevice := !existsAlready || time.Since(ce.when) > CacheLifeTime || ce.instanceID != device.InstanceID
 
 	// Any empty or unspecified addresses should be set to the source address
 	// of the announcement. We also skip any addresses we can't parse.
 
-	l.Debugln("discover: Registering addresses for", id)
+	l.Debugln("discover: Registering addresses for", device.ID)
 	var validAddresses []string
 	for _, addr := range device.Addresses {
 		u, err := url.Parse(addr)
@@ -248,7 +244,7 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
 		}
 	}
 
-	c.Set(id, CacheEntry{
+	c.Set(device.ID, CacheEntry{
 		Addresses:  validAddresses,
 		when:       time.Now(),
 		found:      true,
@@ -257,7 +253,7 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
 
 	if isNewDevice {
 		events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
-			"device": id.String(),
+			"device": device.ID.String(),
 			"addrs":  validAddresses,
 		})
 	}

+ 28 - 25
lib/discover/local.pb.go

@@ -18,6 +18,8 @@ import fmt "fmt"
 import math "math"
 import _ "github.com/gogo/protobuf/gogoproto"
 
+import github_com_syncthing_syncthing_lib_protocol "github.com/syncthing/syncthing/lib/protocol"
+
 import io "io"
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -30,9 +32,9 @@ var _ = math.Inf
 const _ = proto.GoGoProtoPackageIsVersion1
 
 type Announce struct {
-	ID         []byte   `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
-	Addresses  []string `protobuf:"bytes,2,rep,name=addresses" json:"addresses,omitempty"`
-	InstanceID int64    `protobuf:"varint,3,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"`
+	ID         github_com_syncthing_syncthing_lib_protocol.DeviceID `protobuf:"bytes,1,opt,name=id,proto3,customtype=github.com/syncthing/syncthing/lib/protocol.DeviceID" json:"id"`
+	Addresses  []string                                             `protobuf:"bytes,2,rep,name=addresses" json:"addresses,omitempty"`
+	InstanceID int64                                                `protobuf:"varint,3,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"`
 }
 
 func (m *Announce) Reset()                    { *m = Announce{} }
@@ -58,12 +60,14 @@ func (m *Announce) MarshalTo(data []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.ID) > 0 {
-		data[i] = 0xa
-		i++
-		i = encodeVarintLocal(data, i, uint64(len(m.ID)))
-		i += copy(data[i:], m.ID)
+	data[i] = 0xa
+	i++
+	i = encodeVarintLocal(data, i, uint64(m.ID.ProtoSize()))
+	n1, err := m.ID.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
 	}
+	i += n1
 	if len(m.Addresses) > 0 {
 		for _, s := range m.Addresses {
 			data[i] = 0x12
@@ -117,10 +121,8 @@ func encodeVarintLocal(data []byte, offset int, v uint64) int {
 func (m *Announce) ProtoSize() (n int) {
 	var l int
 	_ = l
-	l = len(m.ID)
-	if l > 0 {
-		n += 1 + l + sovLocal(uint64(l))
-	}
+	l = m.ID.ProtoSize()
+	n += 1 + l + sovLocal(uint64(l))
 	if len(m.Addresses) > 0 {
 		for _, s := range m.Addresses {
 			l = len(s)
@@ -201,9 +203,8 @@ func (m *Announce) Unmarshal(data []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.ID = append(m.ID[:0], data[iNdEx:postIndex]...)
-			if m.ID == nil {
-				m.ID = []byte{}
+			if err := m.ID.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
 			}
 			iNdEx = postIndex
 		case 2:
@@ -381,18 +382,20 @@ var (
 )
 
 var fileDescriptorLocal = []byte{
-	// 194 bytes of a gzipped FileDescriptorProto
+	// 235 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0xc9, 0x4f, 0x4e,
 	0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0xc9, 0x2c, 0x4e, 0xce, 0x2f, 0x4b,
 	0x2d, 0x92, 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf,
 	0x4f, 0xcf, 0xd7, 0x07, 0x2b, 0x48, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0xa2, 0x51,
-	0xa9, 0x90, 0x8b, 0xc3, 0x31, 0x2f, 0x2f, 0xbf, 0x34, 0x2f, 0x39, 0x55, 0x48, 0x8c, 0x8b, 0x29,
-	0x33, 0x45, 0x82, 0x51, 0x81, 0x51, 0x83, 0xc7, 0x89, 0xed, 0xd1, 0x3d, 0x79, 0x26, 0x4f, 0x97,
-	0x20, 0xa0, 0x88, 0x90, 0x0c, 0x17, 0x67, 0x62, 0x4a, 0x4a, 0x51, 0x6a, 0x71, 0x71, 0x6a, 0xb1,
-	0x04, 0x93, 0x02, 0xb3, 0x06, 0x67, 0x10, 0x42, 0x40, 0x48, 0x9f, 0x8b, 0x3b, 0x33, 0xaf, 0xb8,
-	0x24, 0x11, 0x68, 0x42, 0x3c, 0x50, 0x3b, 0x33, 0x50, 0x3b, 0xb3, 0x13, 0x1f, 0x50, 0x3b, 0x97,
-	0x27, 0x54, 0x18, 0x68, 0x0c, 0x17, 0x4c, 0x89, 0x67, 0x8a, 0x93, 0xc8, 0x89, 0x87, 0x72, 0x0c,
-	0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x00, 0xe2, 0x07, 0x8f, 0xe4, 0x18, 0x16, 0x3c, 0x96, 0x63, 0x4c,
-	0x62, 0x03, 0xbb, 0xc7, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x91, 0x3f, 0x96, 0x25, 0xd7, 0x00,
-	0x00, 0x00,
+	0x69, 0x2d, 0x23, 0x17, 0x87, 0x63, 0x5e, 0x5e, 0x7e, 0x69, 0x5e, 0x72, 0xaa, 0x50, 0x10, 0x17,
+	0x53, 0x66, 0x8a, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x8f, 0x93, 0xd3, 0x89, 0x7b, 0xf2, 0x0c, 0xb7,
+	0xee, 0xc9, 0x9b, 0x20, 0x99, 0x57, 0x5c, 0x99, 0x97, 0x5c, 0x92, 0x91, 0x99, 0x97, 0x8e, 0xc4,
+	0xca, 0xc9, 0x4c, 0x82, 0x58, 0x91, 0x9c, 0x9f, 0xa3, 0xe7, 0x92, 0x5a, 0x96, 0x99, 0x9c, 0xea,
+	0xe9, 0xf2, 0xe8, 0x9e, 0x3c, 0x93, 0xa7, 0x4b, 0x10, 0xd0, 0x34, 0x21, 0x19, 0x2e, 0xce, 0xc4,
+	0x94, 0x94, 0xa2, 0xd4, 0xe2, 0xe2, 0xd4, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0xce, 0x20, 0x84,
+	0x80, 0x90, 0x3e, 0x17, 0x77, 0x66, 0x5e, 0x71, 0x49, 0x22, 0xd0, 0xf6, 0x78, 0xa0, 0xd5, 0xcc,
+	0x40, 0xab, 0x99, 0x9d, 0xf8, 0x80, 0xda, 0xb9, 0x3c, 0xa1, 0xc2, 0x40, 0x63, 0xb8, 0x60, 0x4a,
+	0x3c, 0x53, 0x9c, 0x44, 0x4e, 0x3c, 0x94, 0x63, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0x02, 0x10, 0x3f,
+	0x78, 0x24, 0xc7, 0xb0, 0xe0, 0xb1, 0x1c, 0x63, 0x12, 0x1b, 0xd8, 0x05, 0xc6, 0x80, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0xa4, 0x46, 0x4f, 0x13, 0x14, 0x01, 0x00, 0x00,
 }

+ 1 - 1
lib/discover/local.proto

@@ -9,7 +9,7 @@ option (gogoproto.sizer_all) = false;
 option (gogoproto.protosizer_all) = true;
 
 message Announce {
-    bytes           id          = 1 [(gogoproto.customname) = "ID"];
+    bytes           id          = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "github.com/syncthing/syncthing/lib/protocol.DeviceID", (gogoproto.nullable) = false];
     repeated string addresses   = 2;
     int64           instance_id = 3 [(gogoproto.customname) = "InstanceID"];
 }

+ 4 - 4
lib/discover/local_test.go

@@ -40,7 +40,7 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
 	src := &net.UDPAddr{IP: []byte{10, 20, 30, 40}, Port: 50}
 
 	new := lc.registerDevice(src, Announce{
-		ID:         []byte{10, 20, 30, 40, 50, 60, 70, 80, 90},
+		ID:         protocol.DeviceID{10, 20, 30, 40, 50, 60, 70, 80, 90},
 		Addresses:  []string{"tcp://0.0.0.0:22000"},
 		InstanceID: 1234567890,
 	})
@@ -50,7 +50,7 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
 	}
 
 	new = lc.registerDevice(src, Announce{
-		ID:         []byte{10, 20, 30, 40, 50, 60, 70, 80, 90},
+		ID:         protocol.DeviceID{10, 20, 30, 40, 50, 60, 70, 80, 90},
 		Addresses:  []string{"tcp://0.0.0.0:22000"},
 		InstanceID: 1234567890,
 	})
@@ -60,7 +60,7 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
 	}
 
 	new = lc.registerDevice(src, Announce{
-		ID:         []byte{42, 10, 20, 30, 40, 50, 60, 70, 80, 90},
+		ID:         protocol.DeviceID{42, 10, 20, 30, 40, 50, 60, 70, 80, 90},
 		Addresses:  []string{"tcp://0.0.0.0:22000"},
 		InstanceID: 1234567890,
 	})
@@ -70,7 +70,7 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) {
 	}
 
 	new = lc.registerDevice(src, Announce{
-		ID:         []byte{10, 20, 30, 40, 50, 60, 70, 80, 90},
+		ID:         protocol.DeviceID{10, 20, 30, 40, 50, 60, 70, 80, 90},
 		Addresses:  []string{"tcp://0.0.0.0:22000"},
 		InstanceID: 91234567890,
 	})

+ 12 - 16
lib/model/model.go

@@ -8,7 +8,6 @@ package model
 
 import (
 	"bufio"
-	"bytes"
 	"crypto/tls"
 	"encoding/json"
 	"errors"
@@ -791,7 +790,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 		var startSequence int64
 
 		for _, dev := range folder.Devices {
-			if bytes.Equal(dev.ID, m.id[:]) {
+			if dev.ID == m.id {
 				// This is the other side's description of what it knows
 				// about us. Lets check to see if we can start sending index
 				// updates directly or need to send the index from start...
@@ -822,7 +821,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 					l.Infof("Device %v folder %q has mismatching index ID for us (%v != %v)", deviceID, folder.ID, dev.IndexID, myIndexID)
 					startSequence = 0
 				}
-			} else if bytes.Equal(dev.ID, deviceID[:]) && dev.IndexID != 0 {
+			} else if dev.ID == deviceID && dev.IndexID != 0 {
 				// This is the other side's description of themselves. We
 				// check to see that it matches the IndexID we have on file,
 				// otherwise we drop our old index data and expect to get a
@@ -885,10 +884,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 
 		nextDevice:
 			for _, device := range folder.Devices {
-				var id protocol.DeviceID
-				copy(id[:], device.ID)
-
-				if _, ok := m.cfg.Devices()[id]; !ok {
+				if _, ok := m.cfg.Devices()[device.ID]; !ok {
 					// The device is currently unknown. Add it to the config.
 
 					addresses := []string{"dynamic"}
@@ -898,9 +894,9 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 						}
 					}
 
-					l.Infof("Adding device %v to config (vouched for by introducer %v)", id, deviceID)
+					l.Infof("Adding device %v to config (vouched for by introducer %v)", device.ID, deviceID)
 					newDeviceCfg := config.DeviceConfiguration{
-						DeviceID:    id,
+						DeviceID:    device.ID,
 						Name:        device.Name,
 						Compression: m.cfg.Devices()[deviceID].Compression,
 						Addresses:   addresses,
@@ -909,7 +905,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 
 					// The introducers' introducers are also our introducers.
 					if device.Introducer {
-						l.Infof("Device %v is now also an introducer", id)
+						l.Infof("Device %v is now also an introducer", device.ID)
 						newDeviceCfg.Introducer = true
 					}
 
@@ -917,7 +913,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 					changed = true
 				}
 
-				for _, er := range m.deviceFolders[id] {
+				for _, er := range m.deviceFolders[device.ID] {
 					if er == folder.ID {
 						// We already share the folder with this device, so
 						// nothing to do.
@@ -928,14 +924,14 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 				// We don't yet share this folder with this device. Add the device
 				// to sharing list of the folder.
 
-				l.Infof("Adding device %v to share %q (vouched for by introducer %v)", id, folder.ID, deviceID)
+				l.Infof("Adding device %v to share %q (vouched for by introducer %v)", device.ID, folder.ID, deviceID)
 
-				m.deviceFolders[id] = append(m.deviceFolders[id], folder.ID)
-				m.folderDevices[folder.ID] = append(m.folderDevices[folder.ID], id)
+				m.deviceFolders[device.ID] = append(m.deviceFolders[device.ID], folder.ID)
+				m.folderDevices[folder.ID] = append(m.folderDevices[folder.ID], device.ID)
 
 				folderCfg := m.cfg.Folders()[folder.ID]
 				folderCfg.Devices = append(folderCfg.Devices, config.FolderDeviceConfiguration{
-					DeviceID: id,
+					DeviceID: device.ID,
 				})
 				m.cfg.SetFolder(folderCfg)
 
@@ -1898,7 +1894,7 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
 			}
 
 			protocolDevice := protocol.Device{
-				ID:          device[:],
+				ID:          device,
 				Name:        deviceCfg.Name,
 				Addresses:   deviceCfg.Addresses,
 				Compression: deviceCfg.Compression,

+ 10 - 10
lib/model/model_test.go

@@ -444,13 +444,13 @@ func TestClusterConfig(t *testing.T) {
 	if l := len(r.Devices); l != 2 {
 		t.Errorf("Incorrect number of devices %d != 2", l)
 	}
-	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
+	if id := r.Devices[0].ID; id != device1 {
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 	}
 	if !r.Devices[0].Introducer {
 		t.Error("Device1 should be flagged as Introducer")
 	}
-	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
+	if id := r.Devices[1].ID; id != device2 {
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 	}
 	if r.Devices[1].Introducer {
@@ -464,13 +464,13 @@ func TestClusterConfig(t *testing.T) {
 	if l := len(r.Devices); l != 2 {
 		t.Errorf("Incorrect number of devices %d != 2", l)
 	}
-	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
+	if id := r.Devices[0].ID; id != device1 {
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 	}
 	if !r.Devices[0].Introducer {
 		t.Error("Device1 should be flagged as Introducer")
 	}
-	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
+	if id := r.Devices[1].ID; id != device2 {
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 	}
 	if r.Devices[1].Introducer {
@@ -1565,8 +1565,8 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
 			{
 				ID: "default",
 				Devices: []protocol.Device{
-					{ID: device1[:]},
-					{ID: device2[:]},
+					{ID: device1},
+					{ID: device2},
 				},
 			},
 		},
@@ -1576,8 +1576,8 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) {
 			{
 				ID: "default",
 				Devices: []protocol.Device{
-					{ID: device1[:]},
-					{ID: device2[:]},
+					{ID: device1},
+					{ID: device2},
 				},
 			},
 		},
@@ -1751,8 +1751,8 @@ func addFakeConn(m *Model, dev protocol.DeviceID) {
 			{
 				ID: "default",
 				Devices: []protocol.Device{
-					{ID: device1[:]},
-					{ID: device2[:]},
+					{ID: device1},
+					{ID: device2},
 				},
 			},
 		},

+ 117 - 118
lib/protocol/bep.pb.go

@@ -255,7 +255,7 @@ func (*Folder) ProtoMessage()               {}
 func (*Folder) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{3} }
 
 type Device struct {
-	ID          []byte      `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	ID          DeviceID    `protobuf:"bytes,1,opt,name=id,proto3,customtype=DeviceID" json:"id"`
 	Name        string      `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
 	Addresses   []string    `protobuf:"bytes,3,rep,name=addresses" json:"addresses,omitempty"`
 	Compression Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression,omitempty"`
@@ -621,12 +621,14 @@ func (m *Device) MarshalTo(data []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
-	if len(m.ID) > 0 {
-		data[i] = 0xa
-		i++
-		i = encodeVarintBep(data, i, uint64(len(m.ID)))
-		i += copy(data[i:], m.ID)
+	data[i] = 0xa
+	i++
+	i = encodeVarintBep(data, i, uint64(m.ID.ProtoSize()))
+	n1, err := m.ID.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
 	}
+	i += n1
 	if len(m.Name) > 0 {
 		data[i] = 0x12
 		i++
@@ -828,11 +830,11 @@ func (m *FileInfo) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x4a
 	i++
 	i = encodeVarintBep(data, i, uint64(m.Version.ProtoSize()))
-	n1, err := m.Version.MarshalTo(data[i:])
+	n2, err := m.Version.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n1
+	i += n2
 	if m.Sequence != 0 {
 		data[i] = 0x50
 		i++
@@ -1112,11 +1114,11 @@ func (m *FileDownloadProgressUpdate) MarshalTo(data []byte) (int, error) {
 	data[i] = 0x1a
 	i++
 	i = encodeVarintBep(data, i, uint64(m.Version.ProtoSize()))
-	n2, err := m.Version.MarshalTo(data[i:])
+	n3, err := m.Version.MarshalTo(data[i:])
 	if err != nil {
 		return 0, err
 	}
-	i += n2
+	i += n3
 	if len(m.BlockIndexes) > 0 {
 		for _, num := range m.BlockIndexes {
 			data[i] = 0x20
@@ -1273,10 +1275,8 @@ func (m *Folder) ProtoSize() (n int) {
 func (m *Device) ProtoSize() (n int) {
 	var l int
 	_ = l
-	l = len(m.ID)
-	if l > 0 {
-		n += 1 + l + sovBep(uint64(l))
-	}
+	l = m.ID.ProtoSize()
+	n += 1 + l + sovBep(uint64(l))
 	l = len(m.Name)
 	if l > 0 {
 		n += 1 + l + sovBep(uint64(l))
@@ -2114,9 +2114,8 @@ func (m *Device) Unmarshal(data []byte) error {
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.ID = append(m.ID[:0], data[iNdEx:postIndex]...)
-			if m.ID == nil {
-				m.ID = []byte{}
+			if err := m.ID.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
 			}
 			iNdEx = postIndex
 		case 2:
@@ -3954,105 +3953,105 @@ var (
 )
 
 var fileDescriptorBep = []byte{
-	// 1589 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0x4f, 0x6f, 0xdb, 0x46,
-	0x16, 0xb7, 0x24, 0xea, 0xdf, 0x48, 0x76, 0xe4, 0x89, 0xe3, 0x68, 0x19, 0xc7, 0xf6, 0x32, 0x09,
-	0xd6, 0x2b, 0x6c, 0x9c, 0xdd, 0x64, 0x77, 0x03, 0x14, 0x68, 0x01, 0x59, 0xa2, 0x1d, 0x21, 0x32,
-	0xa5, 0x50, 0x92, 0xd3, 0xf4, 0x50, 0x41, 0x12, 0x47, 0x32, 0x11, 0x8a, 0xa3, 0x92, 0x54, 0x12,
-	0xf7, 0x23, 0xb4, 0x5f, 0xa0, 0x97, 0x02, 0x41, 0x6e, 0xbd, 0xf7, 0x43, 0xe4, 0x18, 0xe4, 0xd8,
-	0x43, 0xd0, 0xba, 0x97, 0x7e, 0x81, 0xde, 0xfb, 0xf8, 0x86, 0x94, 0x28, 0xff, 0x29, 0x72, 0xe8,
-	0xc1, 0xd0, 0xcc, 0x7b, 0xbf, 0x99, 0x37, 0xf3, 0x7b, 0xbf, 0xf7, 0x86, 0x26, 0xd9, 0x3e, 0x9b,
-	0xec, 0x4e, 0x1c, 0xee, 0x71, 0x9a, 0xc1, 0x9f, 0x01, 0xb7, 0xe4, 0xbb, 0x23, 0xd3, 0x3b, 0x9e,
-	0xf6, 0x77, 0x07, 0x7c, 0x7c, 0x6f, 0xc4, 0x47, 0xfc, 0x1e, 0x7a, 0xfa, 0xd3, 0x21, 0xce, 0x70,
-	0x82, 0x23, 0xb1, 0x50, 0x99, 0x90, 0xe4, 0x23, 0x66, 0x59, 0x9c, 0x6e, 0x91, 0x9c, 0xc1, 0x5e,
-	0x98, 0x03, 0xd6, 0xb5, 0x7b, 0x63, 0x56, 0x8c, 0x6d, 0xc7, 0x76, 0xb2, 0x3a, 0x11, 0x26, 0x0d,
-	0x2c, 0x3e, 0x60, 0x60, 0x99, 0xcc, 0xf6, 0x04, 0x20, 0x2e, 0x00, 0xc2, 0x84, 0x80, 0x3b, 0x64,
-	0x25, 0x00, 0xbc, 0x60, 0x8e, 0x6b, 0x72, 0xbb, 0x98, 0x40, 0xcc, 0xb2, 0xb0, 0x1e, 0x09, 0xa3,
-	0xe2, 0x92, 0xd4, 0x23, 0xd6, 0x33, 0x98, 0x43, 0xff, 0x49, 0x24, 0xef, 0x64, 0x22, 0x62, 0xad,
-	0xdc, 0xbf, 0xb6, 0x1b, 0xde, 0x61, 0xf7, 0x90, 0xb9, 0x6e, 0x6f, 0xc4, 0xda, 0xe0, 0xd4, 0x11,
-	0x42, 0x3f, 0x83, 0xe0, 0x7c, 0x3c, 0x71, 0xc0, 0xe1, 0x6f, 0x1c, 0xc7, 0x15, 0x1b, 0xe7, 0x56,
-	0x54, 0xe6, 0x18, 0x3d, 0xba, 0x40, 0x29, 0x93, 0xe5, 0x8a, 0x35, 0x75, 0x3d, 0xe6, 0x54, 0xb8,
-	0x3d, 0x34, 0x47, 0xf4, 0xdf, 0x24, 0x3d, 0xe4, 0x16, 0x9c, 0xc2, 0x85, 0xf0, 0x89, 0x9d, 0xdc,
-	0xfd, 0xc2, 0x7c, 0xb3, 0x7d, 0x74, 0xec, 0x49, 0x6f, 0x3f, 0x6c, 0x2d, 0xe9, 0x21, 0x4c, 0xf9,
-	0x36, 0x4e, 0x52, 0xc2, 0x43, 0xd7, 0x49, 0xdc, 0x34, 0x04, 0x45, 0x7b, 0xa9, 0xd3, 0x0f, 0x5b,
-	0xf1, 0x5a, 0x55, 0x07, 0x0b, 0x5d, 0x23, 0x49, 0xab, 0xd7, 0x67, 0x56, 0x40, 0x8e, 0x98, 0xd0,
-	0x1b, 0x24, 0xeb, 0xc0, 0x85, 0xbb, 0xdc, 0xb6, 0x4e, 0x90, 0x92, 0x8c, 0x9e, 0xf1, 0x0d, 0x0d,
-	0x98, 0xd3, 0xbb, 0x84, 0x9a, 0x23, 0x9b, 0x3b, 0xac, 0x3b, 0x61, 0xce, 0xd8, 0xc4, 0xd3, 0xba,
-	0x45, 0x09, 0x51, 0xab, 0xc2, 0xd3, 0x9c, 0x3b, 0xe8, 0x2d, 0xb2, 0x1c, 0xc0, 0x0d, 0x66, 0x31,
-	0x8f, 0x15, 0x93, 0x88, 0xcc, 0x0b, 0x63, 0x15, 0x6d, 0x70, 0xb7, 0x35, 0xc3, 0x74, 0x7b, 0x7d,
-	0x8b, 0x75, 0x3d, 0x36, 0x9e, 0x74, 0x4d, 0xdb, 0x60, 0xaf, 0x98, 0x5b, 0x4c, 0x21, 0x96, 0x06,
-	0xbe, 0x36, 0xb8, 0x6a, 0xc2, 0xe3, 0xb3, 0x21, 0x32, 0xed, 0x16, 0x0b, 0x67, 0xd9, 0xa8, 0xa2,
-	0x23, 0x64, 0x23, 0x80, 0x29, 0x6f, 0x80, 0x0d, 0xe1, 0x89, 0xb0, 0x91, 0x5f, 0x60, 0x83, 0x12,
-	0x29, 0xa2, 0x14, 0x1c, 0xd3, 0x0d, 0x92, 0xed, 0x19, 0x86, 0x9f, 0x15, 0x08, 0x95, 0x80, 0x50,
-	0x59, 0x7d, 0x6e, 0xa0, 0x0f, 0x17, 0xb3, 0x2c, 0x9d, 0xd5, 0xc5, 0x65, 0xe9, 0xf5, 0x29, 0x1e,
-	0x30, 0x27, 0x50, 0x66, 0x12, 0xe3, 0x65, 0x7c, 0x03, 0xea, 0xf2, 0xef, 0x24, 0x3f, 0xee, 0xbd,
-	0xea, 0xba, 0xec, 0xab, 0x29, 0xb3, 0x07, 0x0c, 0x69, 0x48, 0xe8, 0x39, 0xb0, 0xb5, 0x02, 0x13,
-	0xdd, 0x24, 0xc4, 0xb4, 0x3d, 0x87, 0x1b, 0x53, 0x58, 0x55, 0x4c, 0x23, 0x4f, 0x11, 0x0b, 0xfd,
-	0x1f, 0xc9, 0x20, 0x89, 0x5d, 0xb8, 0x68, 0x06, 0xbc, 0xd2, 0x9e, 0xec, 0xd3, 0xf1, 0xd3, 0x87,
-	0xad, 0x34, 0x52, 0x58, 0xab, 0x9e, 0xce, 0x87, 0x7a, 0x1a, 0xb1, 0x35, 0x43, 0x69, 0x90, 0x24,
-	0xda, 0x80, 0xa2, 0x94, 0x90, 0x51, 0x50, 0x57, 0xc1, 0x8c, 0xee, 0x92, 0xe4, 0xd0, 0xb4, 0x80,
-	0x8a, 0x38, 0xb2, 0x4e, 0x23, 0x1a, 0x04, 0x73, 0xcd, 0x1e, 0xf2, 0x80, 0x77, 0x01, 0x53, 0x3a,
-	0x24, 0x87, 0x1b, 0x76, 0x26, 0x46, 0xcf, 0x63, 0x7f, 0xd9, 0xb6, 0x6f, 0x12, 0x24, 0x13, 0x7a,
-	0x66, 0x69, 0x8b, 0x45, 0xd2, 0x56, 0x0a, 0x2a, 0x55, 0xd4, 0xdd, 0xfa, 0xf9, 0xfd, 0x22, 0xa5,
-	0x0a, 0xeb, 0x5d, 0xf3, 0x6b, 0x86, 0x4a, 0x4f, 0xe8, 0x38, 0xa6, 0xdb, 0x24, 0x77, 0x56, 0xde,
-	0xcb, 0x7a, 0xd4, 0x44, 0x6f, 0x12, 0x32, 0xe6, 0x86, 0x39, 0x34, 0x99, 0xd1, 0x75, 0x31, 0x85,
-	0x09, 0x3d, 0x1b, 0x5a, 0x5a, 0xb4, 0xe8, 0x0b, 0xd4, 0x17, 0xb7, 0x11, 0xa8, 0x38, 0x9c, 0xfa,
-	0x1e, 0xd3, 0x7e, 0xd1, 0xb3, 0x20, 0x33, 0x22, 0x6f, 0xe1, 0xd4, 0xef, 0x47, 0x36, 0x5f, 0x28,
-	0xab, 0x0c, 0x02, 0x96, 0x6d, 0x1e, 0x2d, 0x29, 0xd0, 0x7e, 0xd8, 0xaf, 0xb2, 0xe0, 0x5f, 0xd0,
-	0xfe, 0x11, 0x1b, 0x78, 0x7c, 0xd6, 0x09, 0x02, 0x18, 0x95, 0x49, 0x66, 0x26, 0x26, 0x82, 0x27,
-	0x9d, 0xcd, 0xfd, 0x2e, 0x39, 0xbb, 0x07, 0x44, 0xcc, 0x81, 0x3b, 0xa9, 0xcf, 0xae, 0xa6, 0xb9,
-	0xf4, 0x3f, 0x24, 0xb5, 0x67, 0xf1, 0xc1, 0xf3, 0xb0, 0xd2, 0xae, 0xce, 0xa3, 0xa1, 0x3d, 0x92,
-	0x9d, 0x54, 0x1f, 0x81, 0x9f, 0x48, 0xdf, 0xbd, 0xde, 0x5a, 0x52, 0x9e, 0x90, 0xec, 0x0c, 0xe0,
-	0x67, 0x9e, 0x0f, 0x87, 0x2e, 0xf3, 0x30, 0x4d, 0x09, 0x3d, 0x98, 0xcd, 0xc8, 0x8f, 0x63, 0x5c,
-	0x41, 0x3e, 0xd8, 0x8e, 0x7b, 0xee, 0x31, 0x26, 0x24, 0xaf, 0xe3, 0x38, 0xd8, 0xf2, 0x53, 0x92,
-	0x12, 0x37, 0xa4, 0x0f, 0x48, 0x66, 0xc0, 0xa7, 0xb6, 0x37, 0xef, 0x87, 0xab, 0xd1, 0xb2, 0x43,
-	0x4f, 0x70, 0xaa, 0x19, 0x50, 0xd9, 0x27, 0xe9, 0xc0, 0x05, 0x5c, 0x87, 0x3d, 0x40, 0xda, 0xbb,
-	0x16, 0x96, 0x46, 0xeb, 0x98, 0x3b, 0x1e, 0x96, 0x46, 0xa4, 0x41, 0x42, 0x6e, 0xa6, 0xe2, 0x7c,
-	0x92, 0x2e, 0x26, 0xca, 0x8f, 0x31, 0x92, 0xd6, 0x7d, 0x02, 0x5d, 0x2f, 0xd2, 0x4c, 0x92, 0x0b,
-	0xcd, 0x64, 0x2e, 0xf5, 0xf8, 0x82, 0xd4, 0x43, 0xb5, 0x26, 0x22, 0x6a, 0x9d, 0x93, 0x23, 0x5d,
-	0x48, 0x4e, 0xf2, 0x02, 0x72, 0x52, 0x73, 0x72, 0x7c, 0xe1, 0x0c, 0x1d, 0x3e, 0xc6, 0xe6, 0xc9,
-	0x9d, 0x9e, 0x73, 0x12, 0x28, 0x6b, 0xd9, 0xb7, 0xb6, 0x43, 0xa3, 0xd2, 0x25, 0x19, 0x9d, 0xb9,
-	0x13, 0xd0, 0x10, 0xbb, 0xf4, 0xd8, 0xb0, 0x3d, 0x54, 0x6a, 0x0f, 0x0f, 0x0d, 0xdb, 0xfb, 0x63,
-	0xfa, 0x0f, 0x22, 0x0d, 0xb8, 0x21, 0x8e, 0xbc, 0x12, 0xcd, 0xbf, 0xea, 0x38, 0x1c, 0xde, 0x27,
-	0x03, 0x2a, 0xc9, 0x07, 0xc0, 0xdb, 0x5c, 0xa8, 0xf2, 0x97, 0xb6, 0xc5, 0x7b, 0x46, 0xd3, 0xe1,
-	0x23, 0xbf, 0xd9, 0x5d, 0x5a, 0xf2, 0x55, 0x92, 0x9e, 0x62, 0x53, 0x08, 0x8b, 0xfe, 0xf6, 0x62,
-	0x91, 0x9e, 0xdd, 0x48, 0x74, 0x90, 0x50, 0xd9, 0xc1, 0x52, 0xe5, 0x7d, 0x8c, 0xc8, 0x97, 0xa3,
-	0x69, 0x8d, 0xe4, 0x04, 0xb2, 0x1b, 0x79, 0xb7, 0x77, 0x3e, 0x26, 0x10, 0xf6, 0x07, 0x32, 0x9d,
-	0x8d, 0x2f, 0x7c, 0x1c, 0x22, 0x95, 0x98, 0xf8, 0xb8, 0x4a, 0x84, 0xe7, 0x10, 0x6b, 0x64, 0xf6,
-	0xc4, 0x49, 0x70, 0xf7, 0xa4, 0x9e, 0xef, 0x8b, 0x42, 0x41, 0x9b, 0x92, 0x22, 0x52, 0xd3, 0xb4,
-	0x47, 0xca, 0x16, 0x49, 0x56, 0x2c, 0x8e, 0xc9, 0x4a, 0xc1, 0xfb, 0xeb, 0x42, 0x98, 0x80, 0x43,
-	0x31, 0x2b, 0xbd, 0x8f, 0x93, 0x5c, 0xe4, 0xd3, 0x03, 0xce, 0xb3, 0x52, 0xa9, 0x77, 0x5a, 0x6d,
-	0x55, 0xef, 0x56, 0x1a, 0xda, 0x7e, 0xed, 0xa0, 0xb0, 0x24, 0x6f, 0x7c, 0xf3, 0xfd, 0x76, 0x71,
-	0x3c, 0x07, 0x2d, 0x7e, 0x55, 0x40, 0x88, 0x9a, 0x56, 0x55, 0x3f, 0x2f, 0xc4, 0xe4, 0x35, 0x00,
-	0x16, 0x22, 0x40, 0xf1, 0x10, 0xfc, 0x8b, 0xe4, 0x11, 0xd0, 0xed, 0x34, 0xab, 0xe5, 0xb6, 0x5a,
-	0x88, 0xcb, 0x32, 0xe0, 0xd6, 0xcf, 0xe2, 0x02, 0xbe, 0x6f, 0x41, 0x5d, 0xa8, 0x4f, 0x3a, 0x6a,
-	0xab, 0x5d, 0x48, 0xc8, 0xeb, 0x00, 0xa4, 0x11, 0x60, 0x58, 0x31, 0x77, 0x40, 0x86, 0x6a, 0xab,
-	0xd9, 0xd0, 0x5a, 0x6a, 0x41, 0x92, 0xaf, 0x03, 0xea, 0xea, 0x02, 0x2a, 0x50, 0xe8, 0xff, 0xc9,
-	0x6a, 0xb5, 0xf1, 0x54, 0xab, 0x37, 0xca, 0xd5, 0x6e, 0x53, 0x6f, 0x1c, 0xc0, 0x9a, 0x56, 0x21,
-	0x29, 0x6f, 0x01, 0xfe, 0x46, 0x04, 0x7f, 0x4e, 0x70, 0x37, 0x81, 0xbd, 0x9a, 0x76, 0x50, 0x48,
-	0xc9, 0x57, 0x01, 0x7a, 0x25, 0x02, 0xf5, 0x49, 0xf5, 0x6f, 0x5c, 0xa9, 0x37, 0x20, 0x74, 0xfa,
-	0xdc, 0x8d, 0x91, 0xec, 0xd2, 0x97, 0x84, 0x9e, 0xff, 0x38, 0xa3, 0xb7, 0x89, 0xa4, 0x35, 0x34,
-	0x15, 0x08, 0xc5, 0xfb, 0x9f, 0x47, 0x68, 0xdc, 0x66, 0x54, 0x21, 0x89, 0xfa, 0x17, 0xff, 0x05,
-	0x32, 0xff, 0x06, 0xa0, 0x6b, 0xe7, 0x41, 0xe0, 0x2c, 0x71, 0x92, 0x8b, 0x6e, 0xac, 0x90, 0xcc,
-	0xa1, 0xda, 0x2e, 0x03, 0xb9, 0x65, 0xd8, 0x1c, 0x8f, 0x14, 0xba, 0x0f, 0x99, 0xd7, 0xc3, 0x02,
-	0xdc, 0x20, 0x49, 0x4d, 0x3d, 0x52, 0x75, 0xd8, 0x78, 0x15, 0x00, 0xcb, 0x21, 0x40, 0x63, 0xa0,
-	0x2b, 0xf8, 0x16, 0x48, 0x95, 0xeb, 0x4f, 0xcb, 0xcf, 0x5a, 0x90, 0x1c, 0x0a, 0xee, 0x95, 0xd0,
-	0x5d, 0xb6, 0x5e, 0xf6, 0x4e, 0xdc, 0xd2, 0xef, 0x31, 0x92, 0x8f, 0x3e, 0x7b, 0xb0, 0x40, 0xda,
-	0xaf, 0xd5, 0xd5, 0x30, 0x5c, 0xd4, 0xe7, 0x8f, 0xe9, 0x0e, 0xc9, 0x56, 0x6b, 0xba, 0x5a, 0x69,
-	0x37, 0xf4, 0x67, 0xe1, 0x5d, 0xa2, 0xa0, 0xaa, 0xe9, 0xa0, 0xb8, 0xfd, 0x8f, 0xc1, 0x7c, 0xeb,
-	0xd9, 0x61, 0xbd, 0xa6, 0x3d, 0xee, 0xe2, 0x8e, 0x71, 0xf9, 0x06, 0x80, 0xaf, 0x47, 0xc1, 0xad,
-	0x93, 0xb1, 0x65, 0xda, 0xcf, 0x71, 0xe3, 0x87, 0x64, 0x35, 0x84, 0xcf, 0x03, 0x24, 0xe4, 0x6d,
-	0x58, 0xb3, 0x71, 0xc1, 0x9a, 0x79, 0x9c, 0x07, 0xe4, 0x4a, 0xb8, 0xb0, 0xa3, 0x3d, 0xd6, 0x40,
-	0x16, 0xa0, 0x9c, 0x4d, 0x58, 0x26, 0x5f, 0xb0, 0xac, 0x63, 0x3f, 0xb7, 0x41, 0x14, 0xa5, 0x1f,
-	0x62, 0x24, 0x3b, 0xeb, 0x50, 0x3e, 0xcf, 0x5a, 0xa3, 0xab, 0xea, 0x7a, 0x43, 0x0f, 0x2f, 0x3e,
-	0x73, 0x6a, 0x1c, 0x87, 0xf0, 0xe1, 0x95, 0x3e, 0x50, 0x35, 0x55, 0xaf, 0x55, 0xc2, 0x7a, 0x98,
-	0x41, 0x0e, 0x98, 0xcd, 0x1c, 0x73, 0x00, 0xff, 0x02, 0xe4, 0x61, 0x9b, 0x56, 0xa7, 0xf2, 0x28,
-	0xbc, 0x31, 0x0a, 0x38, 0xb2, 0x55, 0x6b, 0x3a, 0x38, 0xc6, 0xdb, 0x96, 0xfc, 0xd2, 0x39, 0x2a,
-	0xd7, 0x6b, 0x55, 0x01, 0x4d, 0xc8, 0x45, 0x80, 0xae, 0xcd, 0xa0, 0x35, 0xf1, 0xec, 0xfb, 0xd8,
-	0x92, 0x41, 0x36, 0xff, 0xbc, 0x17, 0xc1, 0x17, 0x49, 0xaa, 0xdc, 0x6c, 0xaa, 0x5a, 0x35, 0x3c,
-	0xfd, 0xdc, 0x57, 0x9e, 0x4c, 0x98, 0x6d, 0xf8, 0x88, 0xfd, 0x86, 0x7e, 0xa0, 0xb6, 0xc3, 0xc3,
-	0xcf, 0x11, 0xfb, 0xdc, 0x19, 0x31, 0x6f, 0x6f, 0xe3, 0xed, 0x2f, 0x9b, 0x4b, 0xef, 0xe0, 0xef,
-	0xed, 0xe9, 0x66, 0xec, 0x1d, 0xfc, 0xfd, 0x7c, 0xba, 0xb9, 0xf4, 0x1b, 0xfc, 0xbe, 0xfe, 0x75,
-	0x33, 0xd6, 0x4f, 0x61, 0xef, 0x7a, 0xf0, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x88, 0xa1,
-	0x6a, 0xa6, 0x0d, 0x00, 0x00,
+	// 1598 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x57, 0xcd, 0x6e, 0xdb, 0xc6,
+	0x16, 0xb6, 0x24, 0xea, 0x6f, 0x24, 0x3b, 0xf2, 0xc4, 0x71, 0x74, 0x19, 0xc7, 0xf6, 0x65, 0x92,
+	0x7b, 0x7d, 0x85, 0x1b, 0xe7, 0xde, 0xa4, 0x6d, 0x80, 0x02, 0x2d, 0x20, 0x4b, 0xb4, 0x23, 0x44,
+	0xa6, 0x14, 0x4a, 0x72, 0x9a, 0x2e, 0x2a, 0x48, 0xe2, 0x48, 0x26, 0x42, 0x71, 0x54, 0x52, 0x4a,
+	0xe2, 0x3e, 0x42, 0xfb, 0x02, 0xdd, 0x14, 0x08, 0xba, 0xeb, 0xb6, 0xe8, 0x43, 0x64, 0x19, 0x64,
+	0xd9, 0x45, 0xd0, 0xa6, 0x9b, 0xbe, 0x40, 0xf7, 0x3d, 0x3c, 0x43, 0x52, 0x94, 0x7f, 0x8a, 0x2c,
+	0xba, 0x30, 0x34, 0x73, 0xce, 0x37, 0xe7, 0xcc, 0xf9, 0xce, 0xcf, 0xd0, 0x24, 0xdb, 0x67, 0x93,
+	0xdd, 0x89, 0xc3, 0xa7, 0x9c, 0x66, 0xf0, 0x67, 0xc0, 0x2d, 0xf9, 0xf6, 0xc8, 0x9c, 0x1e, 0xcf,
+	0xfa, 0xbb, 0x03, 0x3e, 0xbe, 0x33, 0xe2, 0x23, 0x7e, 0x07, 0x35, 0xfd, 0xd9, 0x10, 0x77, 0xb8,
+	0xc1, 0x95, 0x38, 0xa8, 0x4c, 0x48, 0xf2, 0x01, 0xb3, 0x2c, 0x4e, 0xb7, 0x48, 0xce, 0x60, 0xcf,
+	0xcc, 0x01, 0xeb, 0xda, 0xbd, 0x31, 0x2b, 0xc6, 0xb6, 0x63, 0x3b, 0x59, 0x9d, 0x08, 0x91, 0x06,
+	0x12, 0x0f, 0x30, 0xb0, 0x4c, 0x66, 0x4f, 0x05, 0x20, 0x2e, 0x00, 0x42, 0x84, 0x80, 0x5b, 0x64,
+	0xc5, 0x07, 0x3c, 0x63, 0x8e, 0x6b, 0x72, 0xbb, 0x98, 0x40, 0xcc, 0xb2, 0x90, 0x1e, 0x09, 0xa1,
+	0xe2, 0x92, 0xd4, 0x03, 0xd6, 0x33, 0x98, 0x43, 0xff, 0x43, 0xa4, 0xe9, 0xc9, 0x44, 0xf8, 0x5a,
+	0xb9, 0x7b, 0x65, 0x37, 0x88, 0x61, 0xf7, 0x90, 0xb9, 0x6e, 0x6f, 0xc4, 0xda, 0xa0, 0xd4, 0x11,
+	0x42, 0x3f, 0x05, 0xe7, 0x7c, 0x3c, 0x71, 0x40, 0xe1, 0x19, 0x8e, 0xe3, 0x89, 0x8d, 0x33, 0x27,
+	0x2a, 0x73, 0x8c, 0x1e, 0x3d, 0xa0, 0x94, 0xc9, 0x72, 0xc5, 0x9a, 0xb9, 0x53, 0xe6, 0x54, 0xb8,
+	0x3d, 0x34, 0x47, 0xf4, 0x7f, 0x24, 0x3d, 0xe4, 0x16, 0xdc, 0xc2, 0x05, 0xf7, 0x89, 0x9d, 0xdc,
+	0xdd, 0xc2, 0xdc, 0xd8, 0x3e, 0x2a, 0xf6, 0xa4, 0x57, 0x6f, 0xb7, 0x96, 0xf4, 0x00, 0xa6, 0x7c,
+	0x13, 0x27, 0x29, 0xa1, 0xa1, 0xeb, 0x24, 0x6e, 0x1a, 0x82, 0xa2, 0xbd, 0xd4, 0xbb, 0xb7, 0x5b,
+	0xf1, 0x5a, 0x55, 0x07, 0x09, 0x5d, 0x23, 0x49, 0xab, 0xd7, 0x67, 0x96, 0x4f, 0x8e, 0xd8, 0xd0,
+	0x6b, 0x24, 0xeb, 0x40, 0xc0, 0x5d, 0x6e, 0x5b, 0x27, 0x48, 0x49, 0x46, 0xcf, 0x78, 0x82, 0x06,
+	0xec, 0xe9, 0x6d, 0x42, 0xcd, 0x91, 0xcd, 0x1d, 0xd6, 0x9d, 0x30, 0x67, 0x6c, 0xe2, 0x6d, 0xdd,
+	0xa2, 0x84, 0xa8, 0x55, 0xa1, 0x69, 0xce, 0x15, 0xf4, 0x06, 0x59, 0xf6, 0xe1, 0x06, 0xb3, 0xd8,
+	0x94, 0x15, 0x93, 0x88, 0xcc, 0x0b, 0x61, 0x15, 0x65, 0x10, 0xdb, 0x9a, 0x61, 0xba, 0xbd, 0xbe,
+	0xc5, 0xba, 0x53, 0x36, 0x9e, 0x74, 0x4d, 0xdb, 0x60, 0x2f, 0x98, 0x5b, 0x4c, 0x21, 0x96, 0xfa,
+	0xba, 0x36, 0xa8, 0x6a, 0x42, 0xe3, 0xb1, 0x21, 0x32, 0xed, 0x16, 0x0b, 0xa7, 0xd9, 0xa8, 0xa2,
+	0x22, 0x60, 0xc3, 0x87, 0x29, 0x3f, 0x02, 0x1b, 0x42, 0x43, 0xff, 0x15, 0xb2, 0x91, 0xdf, 0x5b,
+	0xf7, 0x50, 0x3f, 0xbf, 0xdd, 0xca, 0x08, 0x5d, 0xad, 0x1a, 0x61, 0x87, 0x12, 0x29, 0x52, 0x39,
+	0xb8, 0xa6, 0x1b, 0x24, 0xdb, 0x33, 0x0c, 0x2f, 0x4b, 0xe0, 0x3a, 0x01, 0xae, 0xb3, 0xfa, 0x5c,
+	0x40, 0xef, 0x2f, 0x66, 0x5d, 0x3a, 0x5d, 0x27, 0x17, 0xa5, 0xdb, 0xa3, 0x7c, 0xc0, 0x1c, 0xbf,
+	0x52, 0x93, 0xe8, 0x2f, 0xe3, 0x09, 0xb0, 0x4e, 0xff, 0x49, 0xf2, 0xe3, 0xde, 0x8b, 0xae, 0xcb,
+	0xbe, 0x9c, 0x31, 0x7b, 0xc0, 0x90, 0x96, 0x84, 0x9e, 0x03, 0x59, 0xcb, 0x17, 0xd1, 0x4d, 0x42,
+	0x4c, 0x7b, 0xea, 0x70, 0x63, 0x06, 0xa7, 0x8a, 0x69, 0xe4, 0x2d, 0x22, 0xa1, 0x1f, 0x92, 0x0c,
+	0x92, 0xda, 0x85, 0xc0, 0x33, 0xa0, 0x95, 0xf6, 0x64, 0x3f, 0xf0, 0x34, 0x52, 0x8a, 0x71, 0x07,
+	0x4b, 0x3d, 0x8d, 0xd8, 0x9a, 0xa1, 0x34, 0x48, 0x12, 0x65, 0x50, 0x40, 0x29, 0x51, 0x56, 0x7e,
+	0x9f, 0xf9, 0x3b, 0xba, 0x4b, 0x92, 0x43, 0xd3, 0x02, 0x2a, 0xe2, 0x98, 0x05, 0x1a, 0xa9, 0x49,
+	0x10, 0xd7, 0xec, 0x21, 0xf7, 0xf3, 0x20, 0x60, 0x4a, 0x87, 0xe4, 0xd0, 0x60, 0x67, 0x62, 0xf4,
+	0x20, 0xf1, 0x7f, 0x97, 0xd9, 0xef, 0x13, 0x24, 0x13, 0x68, 0xc2, 0xb4, 0xc5, 0x22, 0x69, 0x2b,
+	0xf9, 0x9d, 0x2b, 0xfa, 0x70, 0xfd, 0xac, 0xbd, 0x48, 0xeb, 0xc2, 0x79, 0xd7, 0xfc, 0x8a, 0x61,
+	0xe5, 0x27, 0x74, 0x5c, 0xd3, 0x6d, 0x92, 0x3b, 0x5d, 0xee, 0xcb, 0x7a, 0x54, 0x44, 0xaf, 0x13,
+	0x32, 0xe6, 0x86, 0x39, 0x34, 0x99, 0xd1, 0x75, 0x31, 0x85, 0x09, 0x3d, 0x1b, 0x48, 0x5a, 0xb4,
+	0xe8, 0x15, 0xac, 0x57, 0xec, 0x86, 0x5f, 0xd5, 0xc1, 0xd6, 0xd3, 0x98, 0xf6, 0xb3, 0x9e, 0x05,
+	0x99, 0x11, 0x79, 0x0b, 0xb6, 0xde, 0x7c, 0xb2, 0xf9, 0x42, 0x9b, 0x65, 0x10, 0xb0, 0x6c, 0xf3,
+	0x68, 0x8b, 0x41, 0x2f, 0x04, 0xf3, 0x2b, 0x0b, 0xfa, 0x85, 0x5e, 0x38, 0x62, 0x83, 0x29, 0x0f,
+	0x27, 0x83, 0x0f, 0xa3, 0x32, 0xc9, 0x84, 0xc5, 0x44, 0xf0, 0xa6, 0xe1, 0xde, 0x9b, 0x9a, 0x61,
+	0x1c, 0xe0, 0x31, 0x07, 0xea, 0xa4, 0x1e, 0x86, 0xa6, 0xb9, 0xf4, 0xff, 0x24, 0xb5, 0x67, 0xf1,
+	0xc1, 0xd3, 0xa0, 0xf3, 0x2e, 0xcf, 0xbd, 0xa1, 0x3c, 0x92, 0x9d, 0x54, 0x1f, 0x81, 0x1f, 0x4b,
+	0xdf, 0xbe, 0xdc, 0x5a, 0x52, 0x1e, 0x91, 0x6c, 0x08, 0xf0, 0x32, 0xcf, 0x87, 0x43, 0x97, 0x4d,
+	0x31, 0x4d, 0x09, 0xdd, 0xdf, 0x85, 0xe4, 0xc7, 0xd1, 0xaf, 0x20, 0x1f, 0x64, 0xc7, 0x3d, 0xf7,
+	0x18, 0x13, 0x92, 0xd7, 0x71, 0xed, 0x9b, 0xfc, 0x84, 0xa4, 0x44, 0x84, 0xf4, 0x1e, 0xc9, 0x0c,
+	0xf8, 0xcc, 0x9e, 0xce, 0xe7, 0xe3, 0x6a, 0xb4, 0xed, 0x50, 0xe3, 0xdf, 0x2a, 0x04, 0x2a, 0xfb,
+	0x24, 0xed, 0xab, 0x80, 0xeb, 0x60, 0x26, 0x48, 0x7b, 0x57, 0x82, 0xd6, 0x68, 0x1d, 0x73, 0x67,
+	0xba, 0x30, 0x12, 0x60, 0x60, 0x42, 0x6e, 0x66, 0xe2, 0x7e, 0x92, 0x2e, 0x36, 0xca, 0x4f, 0x31,
+	0x92, 0xd6, 0x3d, 0x02, 0xdd, 0x69, 0x64, 0xd4, 0x26, 0x17, 0x46, 0xed, 0xbc, 0xd4, 0xe3, 0x0b,
+	0xa5, 0x1e, 0x54, 0x6b, 0x22, 0x52, 0xad, 0x73, 0x72, 0xa4, 0x73, 0xc9, 0x49, 0x9e, 0x43, 0x4e,
+	0x6a, 0x4e, 0x8e, 0x57, 0x38, 0x43, 0x87, 0x8f, 0x71, 0x98, 0x72, 0xa7, 0xe7, 0x9c, 0xf8, 0x95,
+	0xb5, 0xec, 0x49, 0xdb, 0x81, 0x50, 0xe9, 0x92, 0x8c, 0xce, 0xdc, 0x09, 0xd4, 0x10, 0xbb, 0xf0,
+	0xda, 0x60, 0x1e, 0x3a, 0xb5, 0x87, 0x97, 0x06, 0xf3, 0xde, 0x9a, 0xfe, 0x9b, 0x48, 0x03, 0x6e,
+	0x88, 0x2b, 0xaf, 0x44, 0xf3, 0xaf, 0x3a, 0x0e, 0x87, 0xf7, 0xca, 0x80, 0x4e, 0xf2, 0x00, 0xf0,
+	0x56, 0x17, 0xaa, 0xfc, 0xb9, 0x6d, 0xf1, 0x9e, 0xd1, 0x74, 0xf8, 0xc8, 0x1b, 0x76, 0x17, 0xb6,
+	0x7c, 0x95, 0xa4, 0x67, 0x38, 0x14, 0x82, 0xa6, 0xbf, 0xb9, 0xd8, 0xa4, 0xa7, 0x0d, 0x89, 0x09,
+	0x12, 0x54, 0xb6, 0x7f, 0x54, 0x79, 0x13, 0x23, 0xf2, 0xc5, 0x68, 0x5a, 0x23, 0x39, 0x81, 0xec,
+	0x46, 0xde, 0xf1, 0x9d, 0xf7, 0x71, 0x84, 0xf3, 0x81, 0xcc, 0xc2, 0xf5, 0xb9, 0x8f, 0x43, 0xa4,
+	0x13, 0x13, 0xef, 0xd7, 0x89, 0xf0, 0x3c, 0x62, 0x8f, 0x84, 0x4f, 0x9e, 0x04, 0xb1, 0x27, 0xf5,
+	0x7c, 0x5f, 0x34, 0x0a, 0xca, 0x94, 0x14, 0x91, 0x9a, 0xa6, 0x3d, 0x52, 0xb6, 0x48, 0xb2, 0x62,
+	0x71, 0x4c, 0x56, 0x0a, 0xde, 0x63, 0x17, 0xdc, 0xf8, 0x1c, 0x8a, 0x5d, 0xe9, 0x4d, 0x9c, 0xe4,
+	0x22, 0x9f, 0x22, 0x70, 0x9f, 0x95, 0x4a, 0xbd, 0xd3, 0x6a, 0xab, 0x7a, 0xb7, 0xd2, 0xd0, 0xf6,
+	0x6b, 0x07, 0x85, 0x25, 0x79, 0xe3, 0xeb, 0xef, 0xb6, 0x8b, 0xe3, 0x39, 0x68, 0xf1, 0x2b, 0x03,
+	0x5c, 0xd4, 0xb4, 0xaa, 0xfa, 0x59, 0x21, 0x26, 0xaf, 0x01, 0xb0, 0x10, 0x01, 0x8a, 0x87, 0xe0,
+	0xbf, 0x24, 0x8f, 0x80, 0x6e, 0xa7, 0x59, 0x2d, 0xb7, 0xd5, 0x42, 0x5c, 0x96, 0x01, 0xb7, 0x7e,
+	0x1a, 0xe7, 0xf3, 0x7d, 0x03, 0xfa, 0x42, 0x7d, 0xd4, 0x51, 0x5b, 0xed, 0x42, 0x42, 0x5e, 0x07,
+	0x20, 0x8d, 0x00, 0x83, 0x8e, 0xb9, 0x05, 0x65, 0xa8, 0xb6, 0x9a, 0x0d, 0xad, 0xa5, 0x16, 0x24,
+	0xf9, 0x2a, 0xa0, 0x2e, 0x2f, 0xa0, 0xfc, 0x0a, 0xfd, 0x88, 0xac, 0x56, 0x1b, 0x8f, 0xb5, 0x7a,
+	0xa3, 0x5c, 0xed, 0x36, 0xf5, 0xc6, 0x01, 0x9c, 0x69, 0x15, 0x92, 0xf2, 0x16, 0xe0, 0xaf, 0x45,
+	0xf0, 0x67, 0x0a, 0xee, 0x3a, 0xb0, 0x57, 0xd3, 0x0e, 0x0a, 0x29, 0xf9, 0x32, 0x40, 0x2f, 0x45,
+	0xa0, 0x1e, 0xa9, 0x5e, 0xc4, 0x95, 0x7a, 0x03, 0x5c, 0xa7, 0xcf, 0x44, 0x8c, 0x64, 0x97, 0xbe,
+	0x20, 0xf4, 0xec, 0xc7, 0x1a, 0xbd, 0x49, 0x24, 0xad, 0xa1, 0xa9, 0x40, 0x28, 0xc6, 0x7f, 0x16,
+	0xa1, 0x71, 0x9b, 0x51, 0x85, 0x24, 0xea, 0x9f, 0x7f, 0x00, 0x64, 0xfe, 0x03, 0x40, 0x57, 0xce,
+	0x82, 0x40, 0x59, 0xe2, 0x24, 0x17, 0x35, 0xac, 0x90, 0xcc, 0xa1, 0xda, 0x2e, 0x03, 0xb9, 0x65,
+	0x30, 0x8e, 0x57, 0x0a, 0xd4, 0x87, 0x6c, 0xda, 0xc3, 0x06, 0xdc, 0x20, 0x49, 0x4d, 0x3d, 0x52,
+	0x75, 0x30, 0xbc, 0x0a, 0x80, 0xe5, 0x00, 0xa0, 0x31, 0xa8, 0x2b, 0xf8, 0x16, 0x48, 0x95, 0xeb,
+	0x8f, 0xcb, 0x4f, 0x5a, 0x90, 0x1c, 0x0a, 0xea, 0x95, 0x40, 0x5d, 0xb6, 0x9e, 0xf7, 0x4e, 0xdc,
+	0xd2, 0x1f, 0x31, 0x92, 0x8f, 0x3e, 0x7b, 0x70, 0x40, 0xda, 0xaf, 0xd5, 0xd5, 0xc0, 0x5d, 0x54,
+	0xe7, 0xad, 0xe9, 0x0e, 0xc9, 0x56, 0x6b, 0xba, 0x5a, 0x69, 0x37, 0xf4, 0x27, 0x41, 0x2c, 0x51,
+	0x50, 0xd5, 0x74, 0xb0, 0xb8, 0xbd, 0x8f, 0xc3, 0x7c, 0xeb, 0xc9, 0x61, 0xbd, 0xa6, 0x3d, 0xec,
+	0xa2, 0xc5, 0xb8, 0x7c, 0x0d, 0xc0, 0x57, 0xa3, 0xe0, 0xd6, 0xc9, 0xd8, 0x32, 0xed, 0xa7, 0x68,
+	0xf8, 0x3e, 0x59, 0x0d, 0xe0, 0x73, 0x07, 0x09, 0x79, 0x1b, 0xce, 0x6c, 0x9c, 0x73, 0x66, 0xee,
+	0xe7, 0x1e, 0xb9, 0x14, 0x1c, 0xec, 0x68, 0x0f, 0x35, 0x28, 0x0b, 0xa8, 0x9c, 0x4d, 0x38, 0x26,
+	0x9f, 0x73, 0xac, 0x63, 0x3f, 0xb5, 0xa1, 0x28, 0x4a, 0x3f, 0xc4, 0x48, 0x36, 0x9c, 0x50, 0x1e,
+	0xcf, 0x5a, 0xa3, 0xab, 0xea, 0x7a, 0x43, 0x0f, 0x02, 0x0f, 0x95, 0x1a, 0xc7, 0x25, 0x7c, 0x78,
+	0xa5, 0x0f, 0x54, 0x4d, 0xd5, 0x6b, 0x95, 0xa0, 0x1f, 0x42, 0xc8, 0x01, 0xb3, 0x99, 0x63, 0x0e,
+	0xe0, 0x5f, 0x82, 0x3c, 0x98, 0x69, 0x75, 0x2a, 0x0f, 0x82, 0x88, 0xb1, 0x80, 0x23, 0xa6, 0x5a,
+	0xb3, 0xc1, 0x31, 0x46, 0x5b, 0xf2, 0x5a, 0xe7, 0xa8, 0x5c, 0xaf, 0x55, 0x05, 0x34, 0x21, 0x17,
+	0x01, 0xba, 0x16, 0x42, 0x6b, 0xe2, 0xd9, 0xf7, 0xb0, 0x25, 0x83, 0x6c, 0xfe, 0xf5, 0x2c, 0x82,
+	0x2f, 0x92, 0x54, 0xb9, 0xd9, 0x54, 0xb5, 0x6a, 0x70, 0xfb, 0xb9, 0xae, 0x3c, 0x99, 0x30, 0xdb,
+	0xf0, 0x10, 0xfb, 0x0d, 0xfd, 0x40, 0x6d, 0x07, 0x97, 0x9f, 0x23, 0xf6, 0xb9, 0x33, 0x62, 0xd3,
+	0xbd, 0x8d, 0x57, 0xbf, 0x6e, 0x2e, 0xbd, 0x86, 0xbf, 0x57, 0xef, 0x36, 0x63, 0xaf, 0xe1, 0xef,
+	0x97, 0x77, 0x9b, 0x4b, 0xbf, 0xc3, 0xef, 0xcb, 0xdf, 0x36, 0x63, 0xfd, 0x14, 0xce, 0xae, 0x7b,
+	0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x96, 0x0b, 0xf7, 0x15, 0xb6, 0x0d, 0x00, 0x00,
 }

+ 1 - 1
lib/protocol/bep.proto

@@ -63,7 +63,7 @@ message Folder {
 }
 
 message Device {
-    bytes           id           = 1 [(gogoproto.customname) = "ID"];
+    bytes           id           = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false];
     string          name         = 2;
     repeated string addresses    = 3;
     Compression     compression  = 4;

+ 26 - 1
lib/protocol/deviceid.go

@@ -16,7 +16,9 @@ import (
 	"github.com/calmh/luhn"
 )
 
-type DeviceID [32]byte
+const DeviceIDLength = 32
+
+type DeviceID [DeviceIDLength]byte
 type ShortID uint64
 
 var LocalDeviceID = DeviceID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
@@ -114,6 +116,29 @@ func (n *DeviceID) UnmarshalText(bs []byte) error {
 	}
 }
 
+func (n *DeviceID) ProtoSize() int {
+	// Used by protobuf marshaller.
+	return DeviceIDLength
+}
+
+func (n *DeviceID) MarshalTo(bs []byte) (int, error) {
+	// Used by protobuf marshaller.
+	if len(bs) < DeviceIDLength {
+		return 0, errors.New("destination too short")
+	}
+	copy(bs, (*n)[:])
+	return DeviceIDLength, nil
+}
+
+func (n *DeviceID) Unmarshal(bs []byte) error {
+	// Used by protobuf marshaller.
+	if len(bs) < DeviceIDLength {
+		return errors.New("not enough data")
+	}
+	copy((*n)[:], bs)
+	return nil
+}
+
 func luhnify(s string) (string, error) {
 	if len(s) != 52 {
 		panic("unsupported string length")

+ 53 - 0
lib/protocol/deviceid_test.go

@@ -1,5 +1,8 @@
 // Copyright (C) 2014 The Protocol Authors.
 
+//go:generate go run ../../script/protofmt.go deviceid_test.proto
+//go:generate protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. deviceid_test.proto
+
 package protocol
 
 import "testing"
@@ -96,3 +99,53 @@ func TestDeviceIDFromBytes(t *testing.T) {
 		t.Errorf("Wrong device ID, got %q, want %q", id1, formatted)
 	}
 }
+
+func TestNewDeviceIDMarshalling(t *testing.T) {
+	// The new DeviceID.Unmarshal / DeviceID.MarshalTo serialization should
+	// be message compatible with how we used to serialize DeviceIDs.
+
+	// Create a message with a device ID in old style bytes format
+
+	id0, _ := DeviceIDFromString(formatted)
+	msg0 := TestOldDeviceID{id0[:]}
+
+	//  Marshal it
+
+	bs, err := msg0.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Unmarshal using the new DeviceID.Unmarshal
+
+	var msg1 TestNewDeviceID
+	if err := msg1.Unmarshal(bs); err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify it's the same
+
+	if msg1.Test != id0 {
+		t.Error("Mismatch in old -> new direction")
+	}
+
+	// Marshal using the new DeviceID.MarshalTo
+
+	bs, err = msg1.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Create an old style message and and attempt unmarshal
+
+	var msg2 TestOldDeviceID
+	if err := msg2.Unmarshal(bs); err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify it's the same
+
+	if DeviceIDFromBytes(msg2.Test) != id0 {
+		t.Error("Mismatch in old -> new direction")
+	}
+}

+ 442 - 0
lib/protocol/deviceid_test.pb.go

@@ -0,0 +1,442 @@
+// Code generated by protoc-gen-gogo.
+// source: deviceid_test.proto
+// DO NOT EDIT!
+
+/*
+Package protocol is a generated protocol buffer package.
+
+It is generated from these files:
+	deviceid_test.proto
+
+It has these top-level messages:
+	TestOldDeviceID
+	TestNewDeviceID
+*/
+package protocol
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import _ "github.com/gogo/protobuf/gogoproto"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+type TestOldDeviceID struct {
+	Test []byte `protobuf:"bytes,1,opt,name=test,proto3" json:"test,omitempty"`
+}
+
+func (m *TestOldDeviceID) Reset()                    { *m = TestOldDeviceID{} }
+func (m *TestOldDeviceID) String() string            { return proto.CompactTextString(m) }
+func (*TestOldDeviceID) ProtoMessage()               {}
+func (*TestOldDeviceID) Descriptor() ([]byte, []int) { return fileDescriptorDeviceidTest, []int{0} }
+
+type TestNewDeviceID struct {
+	Test DeviceID `protobuf:"bytes,1,opt,name=test,proto3,customtype=DeviceID" json:"test"`
+}
+
+func (m *TestNewDeviceID) Reset()                    { *m = TestNewDeviceID{} }
+func (m *TestNewDeviceID) String() string            { return proto.CompactTextString(m) }
+func (*TestNewDeviceID) ProtoMessage()               {}
+func (*TestNewDeviceID) Descriptor() ([]byte, []int) { return fileDescriptorDeviceidTest, []int{1} }
+
+func init() {
+	proto.RegisterType((*TestOldDeviceID)(nil), "protocol.TestOldDeviceID")
+	proto.RegisterType((*TestNewDeviceID)(nil), "protocol.TestNewDeviceID")
+}
+func (m *TestOldDeviceID) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *TestOldDeviceID) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Test) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintDeviceidTest(data, i, uint64(len(m.Test)))
+		i += copy(data[i:], m.Test)
+	}
+	return i, nil
+}
+
+func (m *TestNewDeviceID) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *TestNewDeviceID) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	data[i] = 0xa
+	i++
+	i = encodeVarintDeviceidTest(data, i, uint64(m.Test.ProtoSize()))
+	n1, err := m.Test.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n1
+	return i, nil
+}
+
+func encodeFixed64DeviceidTest(data []byte, offset int, v uint64) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	data[offset+4] = uint8(v >> 32)
+	data[offset+5] = uint8(v >> 40)
+	data[offset+6] = uint8(v >> 48)
+	data[offset+7] = uint8(v >> 56)
+	return offset + 8
+}
+func encodeFixed32DeviceidTest(data []byte, offset int, v uint32) int {
+	data[offset] = uint8(v)
+	data[offset+1] = uint8(v >> 8)
+	data[offset+2] = uint8(v >> 16)
+	data[offset+3] = uint8(v >> 24)
+	return offset + 4
+}
+func encodeVarintDeviceidTest(data []byte, offset int, v uint64) int {
+	for v >= 1<<7 {
+		data[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	data[offset] = uint8(v)
+	return offset + 1
+}
+func (m *TestOldDeviceID) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Test)
+	if l > 0 {
+		n += 1 + l + sovDeviceidTest(uint64(l))
+	}
+	return n
+}
+
+func (m *TestNewDeviceID) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = m.Test.ProtoSize()
+	n += 1 + l + sovDeviceidTest(uint64(l))
+	return n
+}
+
+func sovDeviceidTest(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozDeviceidTest(x uint64) (n int) {
+	return sovDeviceidTest(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *TestOldDeviceID) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowDeviceidTest
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: TestOldDeviceID: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: TestOldDeviceID: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Test", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowDeviceidTest
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthDeviceidTest
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Test = append(m.Test[:0], data[iNdEx:postIndex]...)
+			if m.Test == nil {
+				m.Test = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipDeviceidTest(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthDeviceidTest
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *TestNewDeviceID) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowDeviceidTest
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: TestNewDeviceID: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: TestNewDeviceID: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Test", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowDeviceidTest
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthDeviceidTest
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Test.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipDeviceidTest(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthDeviceidTest
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipDeviceidTest(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowDeviceidTest
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := data[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowDeviceidTest
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if data[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+			return iNdEx, nil
+		case 1:
+			iNdEx += 8
+			return iNdEx, nil
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowDeviceidTest
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			iNdEx += length
+			if length < 0 {
+				return 0, ErrInvalidLengthDeviceidTest
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowDeviceidTest
+					}
+					if iNdEx >= l {
+						return 0, io.ErrUnexpectedEOF
+					}
+					b := data[iNdEx]
+					iNdEx++
+					innerWire |= (uint64(b) & 0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				innerWireType := int(innerWire & 0x7)
+				if innerWireType == 4 {
+					break
+				}
+				next, err := skipDeviceidTest(data[start:])
+				if err != nil {
+					return 0, err
+				}
+				iNdEx = start + next
+			}
+			return iNdEx, nil
+		case 4:
+			return iNdEx, nil
+		case 5:
+			iNdEx += 4
+			return iNdEx, nil
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+	}
+	panic("unreachable")
+}
+
+var (
+	ErrInvalidLengthDeviceidTest = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowDeviceidTest   = fmt.Errorf("proto: integer overflow")
+)
+
+var fileDescriptorDeviceidTest = []byte{
+	// 171 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x49, 0x2d, 0xcb,
+	0x4c, 0x4e, 0xcd, 0x4c, 0x89, 0x2f, 0x49, 0x2d, 0x2e, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
+	0xe2, 0x00, 0x53, 0xc9, 0xf9, 0x39, 0x52, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9,
+	0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0xe9, 0xf9, 0xfa, 0x60, 0x99, 0xa4, 0xd2, 0x34, 0x30, 0x0f, 0xcc,
+	0x01, 0xb3, 0x20, 0x1a, 0x95, 0x54, 0xb9, 0xf8, 0x43, 0x80, 0xc6, 0xf8, 0xe7, 0xa4, 0xb8, 0x80,
+	0x8d, 0xf5, 0x74, 0x11, 0x12, 0xe2, 0x62, 0x01, 0x99, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13,
+	0x04, 0x66, 0x2b, 0x99, 0x43, 0x94, 0xf9, 0xa5, 0x96, 0xc3, 0x95, 0xa9, 0x20, 0x2b, 0x73, 0x12,
+	0x38, 0x71, 0x4f, 0x9e, 0xe1, 0xd6, 0x3d, 0x79, 0x0e, 0x98, 0x3c, 0x44, 0xa3, 0x93, 0xcc, 0x89,
+	0x87, 0x72, 0x0c, 0x17, 0x80, 0xf8, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x40, 0xfc, 0xe0, 0x91, 0x1c,
+	0xc3, 0x0b, 0x20, 0x5e, 0xf0, 0x58, 0x8e, 0x31, 0x89, 0x0d, 0xec, 0x08, 0x63, 0x40, 0x00, 0x00,
+	0x00, 0xff, 0xff, 0x35, 0x9c, 0x00, 0x78, 0xd4, 0x00, 0x00, 0x00,
+}

+ 20 - 0
lib/protocol/deviceid_test.proto

@@ -0,0 +1,20 @@
+
+syntax = "proto3";
+
+package protocol;
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.sizer_all) = false;
+option (gogoproto.protosizer_all) = true;
+option (gogoproto.goproto_enum_stringer_all) = false;
+option (gogoproto.goproto_enum_prefix_all) = false;
+
+message TestOldDeviceID {
+    bytes test = 1;
+}
+
+message TestNewDeviceID {
+    bytes test = 1 [(gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false];
+}