Signed envelopes & routing records (#73)
* add SignedEnvelope type
* use struct for SignedEnvelope instead of exposing protobuf directly
* doc comments for envelopes
* tests for SignedEnvelopes
* add helpers to make routing records for Host
* fix doc comment
* go fmt
* add method to peerstore to retrieve signed routing records
* update to match spec changes
* just use nanoseconds
* use proto3 & rename fields to match spec changes
* use proto3 for routing records
* make envelope fields private & validate on unmarshal
* use buffer pool for envelope signatures
* tests for RoutingState
* go fmt
* rename Equals -> Equal, add some comments
* use test helpers
* get rid of unsigned RoutingState struct, only expose SignedRoutingState
* rm batching SignedRoutingStates accessor in peerstore
the datastore peerstore implementation doesn't support batched reads, so
it's no more efficient to get a bunch of states at once than it
is to call SignedRoutingState multiple times.
* whitespace
* expose struct fields & remove accessors
* use camelCase in protos for consistency
* use multiformats uvarint for length-prefixes
* remove payloadType check when unmarhaling
* rm stray ref to golang/protobuf
* define CertifiedAddrBook to avoid breaking API change
* add events for updated addresses and routing state
* remove SignedRoutingStateFromHost helper
moving this to go-libp2p
* add routing state records, extend peerstore API
* fix: rebuild protos with new gogofaster generator
* filter private addrs from signed routing records
* envelope: use byte slices from pool; adjust interface.
* move envelope to record package.
* move protobuf files; adjust imports everywhere.
* rename RoutingStateRecord -> PeerRecord
also removes embedded reference to Envelope from the record,
as that was confusing.
as a result, the CertifiedAddrBook now accepts/returns
record.SignedEnvelope instead of a specialized type.
* hoist Seq from PeerRecord to SignedEnvelope
* test that PeerRecords can't be signed by wrong key
* commit go.sum
* add Seq field to envelope signature
* fix proto_path in Makefile
* fix import ordering
* comments for PeerRecord proto message
also removes the seq field from PeerMessage proto,
since it was moved to the SignedEnvelope
* use Record type for envelope payloads
* rename SignedEnvelope -> Envelope, unmarshal payload in ConsumeEnvelope
* return buffer to pool before early return
* doc comments
* rename CertifiedAddrBook methods, update comments
* cache unmarshalled Record payload inside Envelope
* doc comments
* store reflect.Type when registering Record
* Revert "return buffer to pool before early return"
8d8da386f26482e06dc21989a6b5ade69f0a46d9
misread this - unsigned will be nil if there's an
error, so it was right the way it was
* use a DefaultRecord for unregistered PayloadTypes
instead of returning an error if we don't have a registered
Record for a given PayloadType, we can have a catch-all
DefaultRecord type that just preserves the original payload
as a []byte
* cleanup DefaultRecord code a bit
- removes unused error return from blankRecordForPayloadType
- just references instead of copying in DefaultRecord.UnmarshalRecord
I figure this is likely safe, since we'll be unmarshalling from the
payload of an Envelope, which shouldn't get altered after it's
created.
* use explicit payloadType in MakeEnvelopeWithRecord
* Revert DefaultRecord commits
ae3bc7bdfb657c232229229706854a56effca80b
a26c845a766b45ceabd87c17c0801d191650f0d4
* doc comments
* move Seq field back to PeerRecord
* make diffs optional in EvtLocalAddressesUpdated
* more envelope tests
* replace MakeEnvelope with record.Seal
also:
- add Domain and Codec fields to Record interface
* fix import
* add interface check
* rename ProcessPeerRecord -> ConsumePeerRecord
also, adds bool `accepted` return value
* rename event field, add doc comment
* peer record protobuf: fix field casing.
* record protobuf: add docs and fix casing.
* cleanup: group imports.
* nit: split test/utils.go => test/{addrs,errors}.go.
Co-authored-by: Raúl Kripalani <raul.kripalani@gmail.com>
2020-02-11 03:53:24 +08:00
|
|
|
package record
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"reflect"
|
2022-04-19 03:40:37 +08:00
|
|
|
|
|
|
|
"github.com/libp2p/go-libp2p-core/internal/catch"
|
Signed envelopes & routing records (#73)
* add SignedEnvelope type
* use struct for SignedEnvelope instead of exposing protobuf directly
* doc comments for envelopes
* tests for SignedEnvelopes
* add helpers to make routing records for Host
* fix doc comment
* go fmt
* add method to peerstore to retrieve signed routing records
* update to match spec changes
* just use nanoseconds
* use proto3 & rename fields to match spec changes
* use proto3 for routing records
* make envelope fields private & validate on unmarshal
* use buffer pool for envelope signatures
* tests for RoutingState
* go fmt
* rename Equals -> Equal, add some comments
* use test helpers
* get rid of unsigned RoutingState struct, only expose SignedRoutingState
* rm batching SignedRoutingStates accessor in peerstore
the datastore peerstore implementation doesn't support batched reads, so
it's no more efficient to get a bunch of states at once than it
is to call SignedRoutingState multiple times.
* whitespace
* expose struct fields & remove accessors
* use camelCase in protos for consistency
* use multiformats uvarint for length-prefixes
* remove payloadType check when unmarhaling
* rm stray ref to golang/protobuf
* define CertifiedAddrBook to avoid breaking API change
* add events for updated addresses and routing state
* remove SignedRoutingStateFromHost helper
moving this to go-libp2p
* add routing state records, extend peerstore API
* fix: rebuild protos with new gogofaster generator
* filter private addrs from signed routing records
* envelope: use byte slices from pool; adjust interface.
* move envelope to record package.
* move protobuf files; adjust imports everywhere.
* rename RoutingStateRecord -> PeerRecord
also removes embedded reference to Envelope from the record,
as that was confusing.
as a result, the CertifiedAddrBook now accepts/returns
record.SignedEnvelope instead of a specialized type.
* hoist Seq from PeerRecord to SignedEnvelope
* test that PeerRecords can't be signed by wrong key
* commit go.sum
* add Seq field to envelope signature
* fix proto_path in Makefile
* fix import ordering
* comments for PeerRecord proto message
also removes the seq field from PeerMessage proto,
since it was moved to the SignedEnvelope
* use Record type for envelope payloads
* rename SignedEnvelope -> Envelope, unmarshal payload in ConsumeEnvelope
* return buffer to pool before early return
* doc comments
* rename CertifiedAddrBook methods, update comments
* cache unmarshalled Record payload inside Envelope
* doc comments
* store reflect.Type when registering Record
* Revert "return buffer to pool before early return"
8d8da386f26482e06dc21989a6b5ade69f0a46d9
misread this - unsigned will be nil if there's an
error, so it was right the way it was
* use a DefaultRecord for unregistered PayloadTypes
instead of returning an error if we don't have a registered
Record for a given PayloadType, we can have a catch-all
DefaultRecord type that just preserves the original payload
as a []byte
* cleanup DefaultRecord code a bit
- removes unused error return from blankRecordForPayloadType
- just references instead of copying in DefaultRecord.UnmarshalRecord
I figure this is likely safe, since we'll be unmarshalling from the
payload of an Envelope, which shouldn't get altered after it's
created.
* use explicit payloadType in MakeEnvelopeWithRecord
* Revert DefaultRecord commits
ae3bc7bdfb657c232229229706854a56effca80b
a26c845a766b45ceabd87c17c0801d191650f0d4
* doc comments
* move Seq field back to PeerRecord
* make diffs optional in EvtLocalAddressesUpdated
* more envelope tests
* replace MakeEnvelope with record.Seal
also:
- add Domain and Codec fields to Record interface
* fix import
* add interface check
* rename ProcessPeerRecord -> ConsumePeerRecord
also, adds bool `accepted` return value
* rename event field, add doc comment
* peer record protobuf: fix field casing.
* record protobuf: add docs and fix casing.
* cleanup: group imports.
* nit: split test/utils.go => test/{addrs,errors}.go.
Co-authored-by: Raúl Kripalani <raul.kripalani@gmail.com>
2020-02-11 03:53:24 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrPayloadTypeNotRegistered is returned from ConsumeEnvelope when the Envelope's
|
|
|
|
// PayloadType does not match any registered Record types.
|
|
|
|
ErrPayloadTypeNotRegistered = errors.New("payload type is not registered")
|
|
|
|
|
|
|
|
payloadTypeRegistry = make(map[string]reflect.Type)
|
|
|
|
)
|
|
|
|
|
|
|
|
// Record represents a data type that can be used as the payload of an Envelope.
|
|
|
|
// The Record interface defines the methods used to marshal and unmarshal a Record
|
|
|
|
// type to a byte slice.
|
|
|
|
//
|
|
|
|
// Record types may be "registered" as the default for a given Envelope.PayloadType
|
|
|
|
// using the RegisterType function. Once a Record type has been registered,
|
|
|
|
// an instance of that type will be created and used to unmarshal the payload of
|
|
|
|
// any Envelope with the registered PayloadType when the Envelope is opened using
|
|
|
|
// the ConsumeEnvelope function.
|
|
|
|
//
|
|
|
|
// To use an unregistered Record type instead, use ConsumeTypedEnvelope and pass in
|
|
|
|
// an instance of the Record type that you'd like the Envelope's payload to be
|
|
|
|
// unmarshaled into.
|
|
|
|
type Record interface {
|
|
|
|
|
|
|
|
// Domain is the "signature domain" used when signing and verifying a particular
|
|
|
|
// Record type. The Domain string should be unique to your Record type, and all
|
|
|
|
// instances of the Record type must have the same Domain string.
|
|
|
|
Domain() string
|
|
|
|
|
|
|
|
// Codec is a binary identifier for this type of record, ideally a registered multicodec
|
|
|
|
// (see https://github.com/multiformats/multicodec).
|
|
|
|
// When a Record is put into an Envelope (see record.Seal), the Codec value will be used
|
|
|
|
// as the Envelope's PayloadType. When the Envelope is later unsealed, the PayloadType
|
|
|
|
// will be used to lookup the correct Record type to unmarshal the Envelope payload into.
|
|
|
|
Codec() []byte
|
|
|
|
|
|
|
|
// MarshalRecord converts a Record instance to a []byte, so that it can be used as an
|
|
|
|
// Envelope payload.
|
|
|
|
MarshalRecord() ([]byte, error)
|
|
|
|
|
|
|
|
// UnmarshalRecord unmarshals a []byte payload into an instance of a particular Record type.
|
|
|
|
UnmarshalRecord([]byte) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterType associates a binary payload type identifier with a concrete
|
|
|
|
// Record type. This is used to automatically unmarshal Record payloads from Envelopes
|
|
|
|
// when using ConsumeEnvelope, and to automatically marshal Records and determine the
|
|
|
|
// correct PayloadType when calling Seal.
|
|
|
|
//
|
|
|
|
// Callers must provide an instance of the record type to be registered, which must be
|
|
|
|
// a pointer type. Registration should be done in the init function of the package
|
|
|
|
// where the Record type is defined:
|
|
|
|
//
|
|
|
|
// package hello_record
|
|
|
|
// import record "github.com/libp2p/go-libp2p-core/record"
|
|
|
|
//
|
|
|
|
// func init() {
|
|
|
|
// record.RegisterType(&HelloRecord{})
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// type HelloRecord struct { } // etc..
|
|
|
|
//
|
|
|
|
func RegisterType(prototype Record) {
|
|
|
|
payloadTypeRegistry[string(prototype.Codec())] = getValueType(prototype)
|
|
|
|
}
|
|
|
|
|
2022-04-19 03:40:37 +08:00
|
|
|
func unmarshalRecordPayload(payloadType []byte, payloadBytes []byte) (_rec Record, err error) {
|
|
|
|
defer func() { catch.HandlePanic(recover(), &err, "libp2p envelope record unmarshal") }()
|
|
|
|
|
Signed envelopes & routing records (#73)
* add SignedEnvelope type
* use struct for SignedEnvelope instead of exposing protobuf directly
* doc comments for envelopes
* tests for SignedEnvelopes
* add helpers to make routing records for Host
* fix doc comment
* go fmt
* add method to peerstore to retrieve signed routing records
* update to match spec changes
* just use nanoseconds
* use proto3 & rename fields to match spec changes
* use proto3 for routing records
* make envelope fields private & validate on unmarshal
* use buffer pool for envelope signatures
* tests for RoutingState
* go fmt
* rename Equals -> Equal, add some comments
* use test helpers
* get rid of unsigned RoutingState struct, only expose SignedRoutingState
* rm batching SignedRoutingStates accessor in peerstore
the datastore peerstore implementation doesn't support batched reads, so
it's no more efficient to get a bunch of states at once than it
is to call SignedRoutingState multiple times.
* whitespace
* expose struct fields & remove accessors
* use camelCase in protos for consistency
* use multiformats uvarint for length-prefixes
* remove payloadType check when unmarhaling
* rm stray ref to golang/protobuf
* define CertifiedAddrBook to avoid breaking API change
* add events for updated addresses and routing state
* remove SignedRoutingStateFromHost helper
moving this to go-libp2p
* add routing state records, extend peerstore API
* fix: rebuild protos with new gogofaster generator
* filter private addrs from signed routing records
* envelope: use byte slices from pool; adjust interface.
* move envelope to record package.
* move protobuf files; adjust imports everywhere.
* rename RoutingStateRecord -> PeerRecord
also removes embedded reference to Envelope from the record,
as that was confusing.
as a result, the CertifiedAddrBook now accepts/returns
record.SignedEnvelope instead of a specialized type.
* hoist Seq from PeerRecord to SignedEnvelope
* test that PeerRecords can't be signed by wrong key
* commit go.sum
* add Seq field to envelope signature
* fix proto_path in Makefile
* fix import ordering
* comments for PeerRecord proto message
also removes the seq field from PeerMessage proto,
since it was moved to the SignedEnvelope
* use Record type for envelope payloads
* rename SignedEnvelope -> Envelope, unmarshal payload in ConsumeEnvelope
* return buffer to pool before early return
* doc comments
* rename CertifiedAddrBook methods, update comments
* cache unmarshalled Record payload inside Envelope
* doc comments
* store reflect.Type when registering Record
* Revert "return buffer to pool before early return"
8d8da386f26482e06dc21989a6b5ade69f0a46d9
misread this - unsigned will be nil if there's an
error, so it was right the way it was
* use a DefaultRecord for unregistered PayloadTypes
instead of returning an error if we don't have a registered
Record for a given PayloadType, we can have a catch-all
DefaultRecord type that just preserves the original payload
as a []byte
* cleanup DefaultRecord code a bit
- removes unused error return from blankRecordForPayloadType
- just references instead of copying in DefaultRecord.UnmarshalRecord
I figure this is likely safe, since we'll be unmarshalling from the
payload of an Envelope, which shouldn't get altered after it's
created.
* use explicit payloadType in MakeEnvelopeWithRecord
* Revert DefaultRecord commits
ae3bc7bdfb657c232229229706854a56effca80b
a26c845a766b45ceabd87c17c0801d191650f0d4
* doc comments
* move Seq field back to PeerRecord
* make diffs optional in EvtLocalAddressesUpdated
* more envelope tests
* replace MakeEnvelope with record.Seal
also:
- add Domain and Codec fields to Record interface
* fix import
* add interface check
* rename ProcessPeerRecord -> ConsumePeerRecord
also, adds bool `accepted` return value
* rename event field, add doc comment
* peer record protobuf: fix field casing.
* record protobuf: add docs and fix casing.
* cleanup: group imports.
* nit: split test/utils.go => test/{addrs,errors}.go.
Co-authored-by: Raúl Kripalani <raul.kripalani@gmail.com>
2020-02-11 03:53:24 +08:00
|
|
|
rec, err := blankRecordForPayloadType(payloadType)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = rec.UnmarshalRecord(payloadBytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return rec, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func blankRecordForPayloadType(payloadType []byte) (Record, error) {
|
|
|
|
valueType, ok := payloadTypeRegistry[string(payloadType)]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrPayloadTypeNotRegistered
|
|
|
|
}
|
|
|
|
|
|
|
|
val := reflect.New(valueType)
|
|
|
|
asRecord := val.Interface().(Record)
|
|
|
|
return asRecord, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getValueType(i interface{}) reflect.Type {
|
|
|
|
valueType := reflect.TypeOf(i)
|
|
|
|
if valueType.Kind() == reflect.Ptr {
|
|
|
|
valueType = valueType.Elem()
|
|
|
|
}
|
|
|
|
return valueType
|
|
|
|
}
|