go-libp2p-core/record/record.go

103 lines
3.5 KiB
Go
Raw Normal View History

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"
)
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)
}
func unmarshalRecordPayload(payloadType []byte, payloadBytes []byte) (Record, error) {
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
}