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"

8d8da386f2

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

ae3bc7bdfb
a26c845a76

* 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>
This commit is contained in:
Yusef Napora 2020-02-10 14:53:24 -05:00 committed by GitHub
parent e075dc9f00
commit 7b2888dfdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2446 additions and 50 deletions

View File

@ -4,7 +4,7 @@ GO = $(PB:.proto=.pb.go)
all: $(GO)
%.pb.go: %.proto
protoc --proto_path=$(GOPATH)/src:. --gogofaster_out=. $<
protoc --proto_path=$(PWD)/../..:. --gogofaster_out=. $<
clean:
rm -f *.pb.go

View File

@ -2,6 +2,8 @@ syntax = "proto2";
package crypto.pb;
option go_package = "github.com/libp2p/go-libp2p-core/crypto/pb";
enum KeyType {
RSA = 0;
Ed25519 = 1;

82
event/addrs.go Normal file
View File

@ -0,0 +1,82 @@
package event
import (
"github.com/libp2p/go-libp2p-core/record"
ma "github.com/multiformats/go-multiaddr"
)
// AddrAction represents an action taken on one of a Host's listen addresses.
// It is used to add context to address change events in EvtLocalAddressesUpdated.
type AddrAction int
const (
// Unknown means that the event producer was unable to determine why the address
// is in the current state.
Unknown AddrAction = iota
// Added means that the address is new and was not present prior to the event.
Added
// Maintained means that the address was not altered between the current and
// previous states.
Maintained
// Removed means that the address was removed from the Host.
Removed
)
// UpdatedAddress is used in the EvtLocalAddressesUpdated event to convey
// address change information.
type UpdatedAddress struct {
// Address contains the address that was updated.
Address ma.Multiaddr
// Action indicates what action was taken on the address during the
// event. May be Unknown if the event producer cannot produce diffs.
Action AddrAction
}
// EvtLocalAddressesUpdated should be emitted when the set of listen addresses for
// the local host changes. This may happen for a number of reasons. For example,
// we may have opened a new relay connection, established a new NAT mapping via
// UPnP, or been informed of our observed address by another peer.
//
// EvtLocalAddressesUpdated contains a snapshot of the current listen addresses,
// and may also contain a diff between the current state and the previous state.
// If the event producer is capable of creating a diff, the Diffs field will be
// true, and event consumers can inspect the Action field of each UpdatedAddress
// to see how each address was modified.
//
// For example, the Action will tell you whether an address in
// the Current list was Added by the event producer, or was Maintained without
// changes. Addresses that were removed from the Host will have the AddrAction
// of Removed, and will be in the Removed list.
//
// If the event producer is not capable or producing diffs, the Diffs field will
// be false, the Removed list will always be empty, and the Action for each
// UpdatedAddress in the Current list will be Unknown.
type EvtLocalAddressesUpdated struct {
// Diffs indicates whether this event contains a diff of the Host's previous
// address set.
Diffs bool
// Current contains all current listen addresses for the Host.
// If Diffs == true, the Action field of each UpdatedAddress will tell
// you whether an address was Added, or was Maintained from the previous
// state.
Current []UpdatedAddress
// Removed contains addresses that were removed from the Host.
// This field is only set when Diffs == true.
Removed []UpdatedAddress
}
// EvtLocalPeerRoutingStateUpdated should be emitted when a new signed PeerRecord
// for the local peer has been produced. This will happen whenever the set of listen
// addresses changes.
type EvtLocalPeerRecordUpdated struct {
// Record contains the updated peer.PeerRecord, wrapped in a record.Envelope and
// signed by the Host's private key.
Record *record.Envelope
}

2
go.mod
View File

@ -6,12 +6,14 @@ require (
github.com/gogo/protobuf v1.3.1
github.com/ipfs/go-cid v0.0.4
github.com/jbenet/goprocess v0.1.3
github.com/libp2p/go-buffer-pool v0.0.1
github.com/libp2p/go-flow-metrics v0.0.3
github.com/libp2p/go-openssl v0.0.4
github.com/minio/sha256-simd v0.1.1
github.com/mr-tron/base58 v1.1.3
github.com/multiformats/go-multiaddr v0.2.0
github.com/multiformats/go-multihash v0.0.13
github.com/multiformats/go-varint v0.0.5
github.com/smola/gocompat v0.2.0
go.opencensus.io v0.22.2
)

51
go.sum
View File

@ -1,77 +1,54 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ipfs/go-cid v0.0.3 h1:UIAh32wymBpStoe83YCzwVQQ5Oy/H0FdxvUS6DJDzms=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.4 h1:UlfXKrZx1DjZoBhQHmNHLC1fK1dUJDN20Y28A7s+gJ8=
github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10=
github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89 h1:12K8AlpT0/6QUXSfV0yi4Q0jkbq8NDtIKFtF61AoqV0=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/libp2p/go-buffer-pool v0.0.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg=
github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ=
github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM=
github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs=
github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg=
github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg=
github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@ -83,27 +60,24 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78=
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multiaddr v0.1.2 h1:HWYHNSyyllbQopmVIF5K7JKJugiah+L9/kuZKHbmNdQ=
github.com/multiformats/go-multiaddr v0.1.2/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90=
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
github.com/multiformats/go-multiaddr-net v0.1.1 h1:jFFKUuXTXv+3ARyHZi3XUqQO+YWMKgBdhEvuGRfnL6s=
github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ=
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
github.com/multiformats/go-multihash v0.0.8 h1:wrYcW5yxSi3dU07n5jnuS5PrNwyHy0zRHGVoUugWvXg=
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multihash v0.0.10 h1:lMoNbh2Ssd9PUF74Nz008KGzGPlfeV6wH3rit5IIGCM=
github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
@ -116,13 +90,11 @@ github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smola/gocompat v0.2.0 h1:6b1oIMlUXIpz//VKEDzPVBK8KG7beVwmHIUEBIs/Pns=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -130,9 +102,7 @@ github.com/src-d/envconfig v1.0.0 h1:/AJi6DtjFhZKNx3OB2qMsq7y4yT5//AeSZIe7rk+PX8
github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
@ -141,8 +111,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -154,9 +122,7 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -167,9 +133,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
@ -180,7 +144,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c h1:vamGzbGri8IKo20MQncCuljcQ5uAO6kaCeawQPVblAI=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@ -190,9 +153,7 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d h1:mXa4inJUuWOoA4uEROxtJ3VMELMlVkIxIfcR0HBekAM=
@ -200,8 +161,6 @@ gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVY
gopkg.in/src-d/go-log.v1 v1.0.1 h1:heWvX7J6qbGWbeFS/aRmiy1eYaT+QMV6wNvHDyMjQV4=
gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

11
peer/pb/Makefile Normal file
View File

@ -0,0 +1,11 @@
PB = $(wildcard *.proto)
GO = $(PB:.proto=.pb.go)
all: $(GO)
%.pb.go: %.proto
protoc --proto_path=$(PWD):$(PWD)/../.. --gogofaster_out=. $<
clean:
rm -f *.pb.go
rm -f *.go

606
peer/pb/peer_record.pb.go Normal file
View File

@ -0,0 +1,606 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: peer_record.proto
package peer_pb
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// 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.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// PeerRecord messages contain information that is useful to share with other peers.
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
// is expected to expand to include other information in the future.
//
// PeerRecords are designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
// See https://github.com/libp2p/go-libp2p-core/record/pb/envelope.proto for
// the SignedEnvelope definition.
type PeerRecord struct {
// peer_id contains a libp2p peer id in its binary representation.
PeerId []byte `protobuf:"bytes,1,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"`
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
Seq uint64 `protobuf:"varint,2,opt,name=seq,proto3" json:"seq,omitempty"`
// addresses is a list of public listen addresses for the peer.
Addresses []*PeerRecord_AddressInfo `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses,omitempty"`
}
func (m *PeerRecord) Reset() { *m = PeerRecord{} }
func (m *PeerRecord) String() string { return proto.CompactTextString(m) }
func (*PeerRecord) ProtoMessage() {}
func (*PeerRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_dc0d8059ab0ad14d, []int{0}
}
func (m *PeerRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PeerRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PeerRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PeerRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_PeerRecord.Merge(m, src)
}
func (m *PeerRecord) XXX_Size() int {
return m.Size()
}
func (m *PeerRecord) XXX_DiscardUnknown() {
xxx_messageInfo_PeerRecord.DiscardUnknown(m)
}
var xxx_messageInfo_PeerRecord proto.InternalMessageInfo
func (m *PeerRecord) GetPeerId() []byte {
if m != nil {
return m.PeerId
}
return nil
}
func (m *PeerRecord) GetSeq() uint64 {
if m != nil {
return m.Seq
}
return 0
}
func (m *PeerRecord) GetAddresses() []*PeerRecord_AddressInfo {
if m != nil {
return m.Addresses
}
return nil
}
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
type PeerRecord_AddressInfo struct {
Multiaddr []byte `protobuf:"bytes,1,opt,name=multiaddr,proto3" json:"multiaddr,omitempty"`
}
func (m *PeerRecord_AddressInfo) Reset() { *m = PeerRecord_AddressInfo{} }
func (m *PeerRecord_AddressInfo) String() string { return proto.CompactTextString(m) }
func (*PeerRecord_AddressInfo) ProtoMessage() {}
func (*PeerRecord_AddressInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_dc0d8059ab0ad14d, []int{0, 0}
}
func (m *PeerRecord_AddressInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PeerRecord_AddressInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PeerRecord_AddressInfo.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PeerRecord_AddressInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_PeerRecord_AddressInfo.Merge(m, src)
}
func (m *PeerRecord_AddressInfo) XXX_Size() int {
return m.Size()
}
func (m *PeerRecord_AddressInfo) XXX_DiscardUnknown() {
xxx_messageInfo_PeerRecord_AddressInfo.DiscardUnknown(m)
}
var xxx_messageInfo_PeerRecord_AddressInfo proto.InternalMessageInfo
func (m *PeerRecord_AddressInfo) GetMultiaddr() []byte {
if m != nil {
return m.Multiaddr
}
return nil
}
func init() {
proto.RegisterType((*PeerRecord)(nil), "peer.pb.PeerRecord")
proto.RegisterType((*PeerRecord_AddressInfo)(nil), "peer.pb.PeerRecord.AddressInfo")
}
func init() { proto.RegisterFile("peer_record.proto", fileDescriptor_dc0d8059ab0ad14d) }
var fileDescriptor_dc0d8059ab0ad14d = []byte{
// 189 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0x48, 0x4d, 0x2d,
0x8a, 0x2f, 0x4a, 0x4d, 0xce, 0x2f, 0x4a, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x07,
0x09, 0xe9, 0x15, 0x24, 0x29, 0x2d, 0x66, 0xe4, 0xe2, 0x0a, 0x48, 0x4d, 0x2d, 0x0a, 0x02, 0xcb,
0x0a, 0x89, 0x73, 0x81, 0x65, 0xe2, 0x33, 0x53, 0x24, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0xd8,
0x40, 0x5c, 0xcf, 0x14, 0x21, 0x01, 0x2e, 0xe6, 0xe2, 0xd4, 0x42, 0x09, 0x26, 0x05, 0x46, 0x0d,
0x96, 0x20, 0x10, 0x53, 0xc8, 0x96, 0x8b, 0x33, 0x31, 0x25, 0xa5, 0x28, 0xb5, 0xb8, 0x38, 0xb5,
0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x5e, 0x0f, 0x6a, 0xac, 0x1e, 0xc2, 0x48, 0x3d,
0x47, 0x88, 0x22, 0xcf, 0xbc, 0xb4, 0xfc, 0x20, 0x84, 0x0e, 0x29, 0x6d, 0x2e, 0x6e, 0x24, 0x19,
0x21, 0x19, 0x2e, 0xce, 0xdc, 0xd2, 0x9c, 0x92, 0x4c, 0x90, 0x02, 0xa8, 0xd5, 0x08, 0x01, 0x27,
0x89, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63,
0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x48, 0x62, 0x03, 0xfb, 0xc7, 0x18,
0x10, 0x00, 0x00, 0xff, 0xff, 0xcb, 0x99, 0x56, 0x19, 0xe4, 0x00, 0x00, 0x00,
}
func (m *PeerRecord) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PeerRecord) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PeerRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Addresses) > 0 {
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Addresses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPeerRecord(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if m.Seq != 0 {
i = encodeVarintPeerRecord(dAtA, i, uint64(m.Seq))
i--
dAtA[i] = 0x10
}
if len(m.PeerId) > 0 {
i -= len(m.PeerId)
copy(dAtA[i:], m.PeerId)
i = encodeVarintPeerRecord(dAtA, i, uint64(len(m.PeerId)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *PeerRecord_AddressInfo) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PeerRecord_AddressInfo) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PeerRecord_AddressInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Multiaddr) > 0 {
i -= len(m.Multiaddr)
copy(dAtA[i:], m.Multiaddr)
i = encodeVarintPeerRecord(dAtA, i, uint64(len(m.Multiaddr)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintPeerRecord(dAtA []byte, offset int, v uint64) int {
offset -= sovPeerRecord(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *PeerRecord) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.PeerId)
if l > 0 {
n += 1 + l + sovPeerRecord(uint64(l))
}
if m.Seq != 0 {
n += 1 + sovPeerRecord(uint64(m.Seq))
}
if len(m.Addresses) > 0 {
for _, e := range m.Addresses {
l = e.Size()
n += 1 + l + sovPeerRecord(uint64(l))
}
}
return n
}
func (m *PeerRecord_AddressInfo) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Multiaddr)
if l > 0 {
n += 1 + l + sovPeerRecord(uint64(l))
}
return n
}
func sovPeerRecord(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozPeerRecord(x uint64) (n int) {
return sovPeerRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *PeerRecord) 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 ErrIntOverflowPeerRecord
}
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: PeerRecord: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PeerRecord: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PeerId", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthPeerRecord
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthPeerRecord
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PeerId = append(m.PeerId[:0], dAtA[iNdEx:postIndex]...)
if m.PeerId == nil {
m.PeerId = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Seq", wireType)
}
m.Seq = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Seq |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPeerRecord
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPeerRecord
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Addresses = append(m.Addresses, &PeerRecord_AddressInfo{})
if err := m.Addresses[len(m.Addresses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPeerRecord(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPeerRecord
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPeerRecord
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PeerRecord_AddressInfo) 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 ErrIntOverflowPeerRecord
}
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: AddressInfo: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: AddressInfo: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Multiaddr", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthPeerRecord
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthPeerRecord
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Multiaddr = append(m.Multiaddr[:0], dAtA[iNdEx:postIndex]...)
if m.Multiaddr == nil {
m.Multiaddr = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPeerRecord(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPeerRecord
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPeerRecord
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipPeerRecord(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPeerRecord
}
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, ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPeerRecord
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthPeerRecord
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupPeerRecord
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthPeerRecord
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthPeerRecord = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowPeerRecord = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupPeerRecord = fmt.Errorf("proto: unexpected end of group")
)

29
peer/pb/peer_record.proto Normal file
View File

@ -0,0 +1,29 @@
syntax = "proto3";
package peer.pb;
// PeerRecord messages contain information that is useful to share with other peers.
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
// is expected to expand to include other information in the future.
//
// PeerRecords are designed to be serialized to bytes and placed inside of
// SignedEnvelopes before sharing with other peers.
// See https://github.com/libp2p/go-libp2p-core/record/pb/envelope.proto for
// the SignedEnvelope definition.
message PeerRecord {
// AddressInfo is a wrapper around a binary multiaddr. It is defined as a
// separate message to allow us to add per-address metadata in the future.
message AddressInfo {
bytes multiaddr = 1;
}
// peer_id contains a libp2p peer id in its binary representation.
bytes peer_id = 1;
// seq contains a monotonically-increasing sequence counter to order PeerRecords in time.
uint64 seq = 2;
// addresses is a list of public listen addresses for the peer.
repeated AddressInfo addresses = 3;
}

208
peer/record.go Normal file
View File

@ -0,0 +1,208 @@
package peer
import (
"fmt"
"time"
pb "github.com/libp2p/go-libp2p-core/peer/pb"
"github.com/libp2p/go-libp2p-core/record"
ma "github.com/multiformats/go-multiaddr"
"github.com/gogo/protobuf/proto"
)
var _ record.Record = (*PeerRecord)(nil)
func init() {
record.RegisterType(&PeerRecord{})
}
// The domain string used for peer records contained in a Envelope.
const PeerRecordEnvelopeDomain = "libp2p-peer-record"
// The type hint used to identify peer records in a Envelope.
// Defined in https://github.com/multiformats/multicodec/blob/master/table.csv
// with name "libp2p-peer-record"
var PeerRecordEnvelopePayloadType = []byte{0x03, 0x01}
// PeerRecord contains information that is broadly useful to share with other peers,
// either through a direct exchange (as in the libp2p identify protocol), or through
// a Peer Routing provider, such as a DHT.
//
// Currently, a PeerRecord contains the public listen addresses for a peer, but this
// is expected to expand to include other information in the future.
//
// PeerRecords are ordered in time by their Seq field. Newer PeerRecords must have
// greater Seq values than older records. The NewPeerRecord function will create
// a PeerRecord with a timestamp-based Seq value. The other PeerRecord fields should
// be set by the caller:
//
// rec := peer.NewPeerRecord()
// rec.PeerID = aPeerID
// rec.Addrs = someAddrs
//
// Alternatively, you can construct a PeerRecord struct directly and use the TimestampSeq
// helper to set the Seq field:
//
// rec := peer.PeerRecord{PeerID: aPeerID, Addrs: someAddrs, Seq: peer.TimestampSeq()}
//
// Failing to set the Seq field will not result in an error, however, a PeerRecord with a
// Seq value of zero may be ignored or rejected by other peers.
//
// PeerRecords are intended to be shared with other peers inside a signed
// routing.Envelope, and PeerRecord implements the routing.Record interface
// to facilitate this.
//
// To share a PeerRecord, first call Sign to wrap the record in a Envelope
// and sign it with the local peer's private key:
//
// rec := &PeerRecord{PeerID: myPeerId, Addrs: myAddrs}
// envelope, err := rec.Sign(myPrivateKey)
//
// The resulting record.Envelope can be marshalled to a []byte and shared
// publicly. As a convenience, the MarshalSigned method will produce the
// Envelope and marshal it to a []byte in one go:
//
// rec := &PeerRecord{PeerID: myPeerId, Addrs: myAddrs}
// recordBytes, err := rec.MarshalSigned(myPrivateKey)
//
// To validate and unmarshal a signed PeerRecord from a remote peer,
// "consume" the containing envelope, which will return both the
// routing.Envelope and the inner Record. The Record must be cast to
// a PeerRecord pointer before use:
//
// envelope, untypedRecord, err := ConsumeEnvelope(envelopeBytes, PeerRecordEnvelopeDomain)
// if err != nil {
// handleError(err)
// return
// }
// peerRec := untypedRecord.(*PeerRecord)
//
type PeerRecord struct {
// PeerID is the ID of the peer this record pertains to.
PeerID ID
// Addrs contains the public addresses of the peer this record pertains to.
Addrs []ma.Multiaddr
// Seq is a monotonically-increasing sequence counter that's used to order
// PeerRecords in time. The interval between Seq values is unspecified,
// but newer PeerRecords MUST have a greater Seq value than older records
// for the same peer.
Seq uint64
}
// NewPeerRecord returns a PeerRecord with a timestamp-based sequence number.
// The returned record is otherwise empty and should be populated by the caller.
func NewPeerRecord() *PeerRecord {
return &PeerRecord{Seq: TimestampSeq()}
}
// PeerRecordFromAddrInfo creates a PeerRecord from an AddrInfo struct.
// The returned record will have a timestamp-based sequence number.
func PeerRecordFromAddrInfo(info AddrInfo) *PeerRecord {
rec := NewPeerRecord()
rec.PeerID = info.ID
rec.Addrs = info.Addrs
return rec
}
// TimestampSeq is a helper to generate a timestamp-based sequence number for a PeerRecord.
func TimestampSeq() uint64 {
return uint64(time.Now().UnixNano())
}
// Domain is used when signing and validating PeerRecords contained in Envelopes.
// It is constant for all PeerRecord instances.
func (r *PeerRecord) Domain() string {
return PeerRecordEnvelopeDomain
}
// Codec is a binary identifier for the PeerRecord type. It is constant for all PeerRecord instances.
func (r *PeerRecord) Codec() []byte {
return PeerRecordEnvelopePayloadType
}
// UnmarshalRecord parses a PeerRecord from a byte slice.
// This method is called automatically when consuming a record.Envelope
// whose PayloadType indicates that it contains a PeerRecord.
// It is generally not necessary or recommended to call this method directly.
func (r *PeerRecord) UnmarshalRecord(bytes []byte) error {
if r == nil {
return fmt.Errorf("cannot unmarshal PeerRecord to nil receiver")
}
var msg pb.PeerRecord
err := proto.Unmarshal(bytes, &msg)
if err != nil {
return err
}
var id ID
err = id.UnmarshalBinary(msg.PeerId)
if err != nil {
return err
}
r.PeerID = id
r.Addrs = addrsFromProtobuf(msg.Addresses)
r.Seq = msg.Seq
return nil
}
// MarshalRecord serializes a PeerRecord to a byte slice.
// This method is called automatically when constructing a routing.Envelope
// using Seal or PeerRecord.Sign.
func (r *PeerRecord) MarshalRecord() ([]byte, error) {
idBytes, err := r.PeerID.MarshalBinary()
if err != nil {
return nil, err
}
msg := pb.PeerRecord{
PeerId: idBytes,
Addresses: addrsToProtobuf(r.Addrs),
Seq: r.Seq,
}
return proto.Marshal(&msg)
}
// Equal returns true if the other PeerRecord is identical to this one.
func (r *PeerRecord) Equal(other *PeerRecord) bool {
if other == nil {
return r == nil
}
if r.PeerID != other.PeerID {
return false
}
if r.Seq != other.Seq {
return false
}
if len(r.Addrs) != len(other.Addrs) {
return false
}
for i, _ := range r.Addrs {
if !r.Addrs[i].Equal(other.Addrs[i]) {
return false
}
}
return true
}
func addrsFromProtobuf(addrs []*pb.PeerRecord_AddressInfo) []ma.Multiaddr {
var out []ma.Multiaddr
for _, addr := range addrs {
a, err := ma.NewMultiaddrBytes(addr.Multiaddr)
if err != nil {
continue
}
out = append(out, a)
}
return out
}
func addrsToProtobuf(addrs []ma.Multiaddr) []*pb.PeerRecord_AddressInfo {
var out []*pb.PeerRecord_AddressInfo
for _, addr := range addrs {
out = append(out, &pb.PeerRecord_AddressInfo{Multiaddr: addr.Bytes()})
}
return out
}

54
peer/record_test.go Normal file
View File

@ -0,0 +1,54 @@
package peer_test
import (
"bytes"
"testing"
"github.com/libp2p/go-libp2p-core/crypto"
. "github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/record"
"github.com/libp2p/go-libp2p-core/test"
)
func TestPeerRecordConstants(t *testing.T) {
msgf := "Changing the %s may cause peer records to be incompatible with older versions. " +
"If you've already thought that through, please update this test so that it passes with the new values."
rec := PeerRecord{}
if rec.Domain() != "libp2p-peer-record" {
t.Errorf(msgf, "signing domain")
}
if !bytes.Equal(rec.Codec(), []byte{0x03, 0x01}) {
t.Errorf(msgf, "codec value")
}
}
func TestSignedPeerRecordFromEnvelope(t *testing.T) {
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
test.AssertNilError(t, err)
addrs := test.GenerateTestAddrs(10)
id, err := IDFromPrivateKey(priv)
test.AssertNilError(t, err)
rec := &PeerRecord{PeerID: id, Addrs: addrs, Seq: TimestampSeq()}
envelope, err := record.Seal(rec, priv)
test.AssertNilError(t, err)
t.Run("is unaltered after round-trip serde", func(t *testing.T) {
envBytes, err := envelope.Marshal()
test.AssertNilError(t, err)
env2, untypedRecord, err := record.ConsumeEnvelope(envBytes, PeerRecordEnvelopeDomain)
test.AssertNilError(t, err)
rec2, ok := untypedRecord.(*PeerRecord)
if !ok {
t.Error("unmarshaled record is not a *PeerRecord")
}
if !rec.Equal(rec2) {
t.Error("expected peer record to be unaltered after round-trip serde")
}
if !envelope.Equal(env2) {
t.Error("expected signed envelope to be unchanged after round-trip serde")
}
})
}

View File

@ -9,9 +9,9 @@ import (
"math"
"time"
"github.com/libp2p/go-libp2p-core/peer"
ic "github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/record"
ma "github.com/multiformats/go-multiaddr"
)
@ -107,7 +107,7 @@ type AddrBook interface {
// the given oldTTL to have the given newTTL.
UpdateAddrs(p peer.ID, oldTTL time.Duration, newTTL time.Duration)
// Addresses returns all known (and valid) addresses for a given peer
// Addrs returns all known (and valid) addresses for a given peer.
Addrs(p peer.ID) []ma.Multiaddr
// AddrStream returns a channel that gets all addresses for a given
@ -122,6 +122,80 @@ type AddrBook interface {
PeersWithAddrs() peer.IDSlice
}
// CertifiedAddrBook manages "self-certified" addresses for remote peers.
// Self-certified addresses are contained in peer.PeerRecords
// which are wrapped in a record.Envelope and signed by the peer
// to whom they belong.
//
// Certified addresses (CA) are generally more secure than uncertified
// addresses (UA). Consequently, CAs beat and displace UAs. When the
// peerstore learns CAs for a peer, it will reject UAs for the same peer
// (as long as the former haven't expired).
// Furthermore, peer records act like sequenced snapshots of CAs. Therefore,
// processing a peer record that's newer than the last one seen overwrites
// all addresses with the incoming ones.
//
// This interface is most useful when combined with AddrBook.
// To test whether a given AddrBook / Peerstore implementation supports
// certified addresses, callers should use the GetCertifiedAddrBook helper or
// type-assert on the CertifiedAddrBook interface:
//
// if cab, ok := aPeerstore.(CertifiedAddrBook); ok {
// cab.ConsumePeerRecord(signedPeerRecord, aTTL)
// }
//
type CertifiedAddrBook interface {
// ConsumePeerRecord adds addresses from a signed peer.PeerRecord (contained in
// a record.Envelope), which will expire after the given TTL.
//
// The 'accepted' return value indicates that the record was successfully processed
// and integrated into the CertifiedAddrBook state. If 'accepted' is false but no
// error is returned, it means that the record was ignored, most likely because
// a newer record exists for the same peer.
//
// Signed records added via this method will be stored without
// alteration as long as the address TTLs remain valid. The Envelopes
// containing the PeerRecords can be retrieved by calling GetPeerRecord(peerID).
//
// If the signed PeerRecord belongs to a peer that already has certified
// addresses in the CertifiedAddrBook, the new addresses will replace the
// older ones, iff the new record has a higher sequence number than the
// existing record. Attempting to add a peer record with a
// sequence number that's <= an existing record for the same peer will not
// result in an error, but the record will be ignored, and the 'accepted'
// bool return value will be false.
//
// If the CertifiedAddrBook is also an AddrBook (which is most likely the case),
// adding certified addresses for a peer will *replace* any
// existing non-certified addresses for that peer, and only the certified
// addresses will be returned from AddrBook.Addrs thereafter.
//
// Likewise, once certified addresses have been added for a given peer,
// any non-certified addresses added via AddrBook.AddAddrs or
// AddrBook.SetAddrs will be ignored. AddrBook.SetAddrs may still be used
// to update the TTL of certified addresses that have previously been
// added via ConsumePeerRecord.
ConsumePeerRecord(s *record.Envelope, ttl time.Duration) (accepted bool, err error)
// GetPeerRecord returns a Envelope containing a PeerRecord for the
// given peer id, if one exists.
// Returns nil if no signed PeerRecord exists for the peer.
GetPeerRecord(p peer.ID) *record.Envelope
}
// GetCertifiedAddrBook is a helper to "upcast" an AddrBook to a
// CertifiedAddrBook by using type assertion. If the given AddrBook
// is also a CertifiedAddrBook, it will be returned, and the ok return
// value will be true. Returns (nil, false) if the AddrBook is not a
// CertifiedAddrBook.
//
// Note that since Peerstore embeds the AddrBook interface, you can also
// call GetCertifiedAddrBook(myPeerstore).
func GetCertifiedAddrBook(ab AddrBook) (cab CertifiedAddrBook, ok bool) {
cab, ok = ab.(CertifiedAddrBook)
return cab, ok
}
// KeyBook tracks the keys of Peers.
type KeyBook interface {
// PubKey stores the public key of a peer.

297
record/envelope.go Normal file
View File

@ -0,0 +1,297 @@
package record
import (
"bytes"
"errors"
"fmt"
"sync"
"github.com/libp2p/go-libp2p-core/crypto"
pb "github.com/libp2p/go-libp2p-core/record/pb"
pool "github.com/libp2p/go-buffer-pool"
"github.com/gogo/protobuf/proto"
"github.com/multiformats/go-varint"
)
// Envelope contains an arbitrary []byte payload, signed by a libp2p peer.
//
// Envelopes are signed in the context of a particular "domain", which is a
// string specified when creating and verifying the envelope. You must know the
// domain string used to produce the envelope in order to verify the signature
// and access the payload.
type Envelope struct {
// The public key that can be used to verify the signature and derive the peer id of the signer.
PublicKey crypto.PubKey
// A binary identifier that indicates what kind of data is contained in the payload.
// TODO(yusef): enforce multicodec prefix
PayloadType []byte
// The envelope payload.
RawPayload []byte
// The signature of the domain string :: type hint :: payload.
signature []byte
// the unmarshalled payload as a Record, cached on first access via the Record accessor method
cached Record
unmarshalError error
unmarshalOnce sync.Once
}
var ErrEmptyDomain = errors.New("envelope domain must not be empty")
var ErrEmptyPayloadType = errors.New("payloadType must not be empty")
var ErrInvalidSignature = errors.New("invalid signature or incorrect domain")
// Seal marshals the given Record, places the marshaled bytes inside an Envelope,
// and signs with the given private key.
func Seal(rec Record, privateKey crypto.PrivKey) (*Envelope, error) {
payload, err := rec.MarshalRecord()
if err != nil {
return nil, fmt.Errorf("error marshaling record: %v", err)
}
domain := rec.Domain()
payloadType := rec.Codec()
if domain == "" {
return nil, ErrEmptyDomain
}
if len(payloadType) == 0 {
return nil, ErrEmptyPayloadType
}
unsigned, err := makeUnsigned(domain, payloadType, payload)
if err != nil {
return nil, err
}
defer pool.Put(unsigned)
sig, err := privateKey.Sign(unsigned)
if err != nil {
return nil, err
}
return &Envelope{
PublicKey: privateKey.GetPublic(),
PayloadType: payloadType,
RawPayload: payload,
signature: sig,
}, nil
}
// ConsumeEnvelope unmarshals a serialized Envelope and validates its
// signature using the provided 'domain' string. If validation fails, an error
// is returned, along with the unmarshalled envelope so it can be inspected.
//
// On success, ConsumeEnvelope returns the Envelope itself, as well as the inner payload,
// unmarshalled into a concrete Record type. The actual type of the returned Record depends
// on what has been registered for the Envelope's PayloadType (see RegisterType for details).
//
// You can type assert on the returned Record to convert it to an instance of the concrete
// Record type:
//
// envelope, rec, err := ConsumeEnvelope(envelopeBytes, peer.PeerRecordEnvelopeDomain)
// if err != nil {
// handleError(envelope, err) // envelope may be non-nil, even if errors occur!
// return
// }
// peerRec, ok := rec.(*peer.PeerRecord)
// if ok {
// doSomethingWithPeerRecord(peerRec)
// }
//
// Important: you MUST check the error value before using the returned Envelope. In some error
// cases, including when the envelope signature is invalid, both the Envelope and an error will
// be returned. This allows you to inspect the unmarshalled but invalid Envelope. As a result,
// you must not assume that any non-nil Envelope returned from this function is valid.
//
// If the Envelope signature is valid, but no Record type is registered for the Envelope's
// PayloadType, ErrPayloadTypeNotRegistered will be returned, along with the Envelope and
// a nil Record.
func ConsumeEnvelope(data []byte, domain string) (envelope *Envelope, rec Record, err error) {
e, err := UnmarshalEnvelope(data)
if err != nil {
return nil, nil, fmt.Errorf("failed when unmarshalling the envelope: %w", err)
}
err = e.validate(domain)
if err != nil {
return e, nil, fmt.Errorf("failed to validate envelope: %w", err)
}
rec, err = e.Record()
if err != nil {
return e, nil, fmt.Errorf("failed to unmarshal envelope payload: %w", err)
}
return e, rec, nil
}
// ConsumeTypedEnvelope unmarshals a serialized Envelope and validates its
// signature. If validation fails, an error is returned, along with the unmarshalled
// envelope so it can be inspected.
//
// Unlike ConsumeEnvelope, ConsumeTypedEnvelope does not try to automatically determine
// the type of Record to unmarshal the Envelope's payload into. Instead, the caller provides
// a destination Record instance, which will unmarshal the Envelope payload. It is the caller's
// responsibility to determine whether the given Record type is able to unmarshal the payload
// correctly.
//
// rec := &MyRecordType{}
// envelope, err := ConsumeTypedEnvelope(envelopeBytes, rec)
// if err != nil {
// handleError(envelope, err)
// }
// doSomethingWithRecord(rec)
//
// Important: you MUST check the error value before using the returned Envelope. In some error
// cases, including when the envelope signature is invalid, both the Envelope and an error will
// be returned. This allows you to inspect the unmarshalled but invalid Envelope. As a result,
// you must not assume that any non-nil Envelope returned from this function is valid.
func ConsumeTypedEnvelope(data []byte, destRecord Record) (envelope *Envelope, err error) {
e, err := UnmarshalEnvelope(data)
if err != nil {
return nil, fmt.Errorf("failed when unmarshalling the envelope: %w", err)
}
err = e.validate(destRecord.Domain())
if err != nil {
return e, fmt.Errorf("failed to validate envelope: %w", err)
}
err = destRecord.UnmarshalRecord(e.RawPayload)
if err != nil {
return e, fmt.Errorf("failed to unmarshal envelope payload: %w", err)
}
e.cached = destRecord
return e, nil
}
// UnmarshalEnvelope unmarshals a serialized Envelope protobuf message,
// without validating its contents. Most users should use ConsumeEnvelope.
func UnmarshalEnvelope(data []byte) (*Envelope, error) {
var e pb.Envelope
if err := proto.Unmarshal(data, &e); err != nil {
return nil, err
}
key, err := crypto.PublicKeyFromProto(e.PublicKey)
if err != nil {
return nil, err
}
return &Envelope{
PublicKey: key,
PayloadType: e.PayloadType,
RawPayload: e.Payload,
signature: e.Signature,
}, nil
}
// Marshal returns a byte slice containing a serialized protobuf representation
// of a Envelope.
func (e *Envelope) Marshal() ([]byte, error) {
key, err := crypto.PublicKeyToProto(e.PublicKey)
if err != nil {
return nil, err
}
msg := pb.Envelope{
PublicKey: key,
PayloadType: e.PayloadType,
Payload: e.RawPayload,
Signature: e.signature,
}
return proto.Marshal(&msg)
}
// Equal returns true if the other Envelope has the same public key,
// payload, payload type, and signature. This implies that they were also
// created with the same domain string.
func (e *Envelope) Equal(other *Envelope) bool {
if other == nil {
return e == nil
}
return e.PublicKey.Equals(other.PublicKey) &&
bytes.Compare(e.PayloadType, other.PayloadType) == 0 &&
bytes.Compare(e.signature, other.signature) == 0 &&
bytes.Compare(e.RawPayload, other.RawPayload) == 0
}
// Record returns the Envelope's payload unmarshalled as a Record.
// The concrete type of the returned Record depends on which Record
// type was registered for the Envelope's PayloadType - see record.RegisterType.
//
// Once unmarshalled, the Record is cached for future access.
func (e *Envelope) Record() (Record, error) {
e.unmarshalOnce.Do(func() {
if e.cached != nil {
return
}
e.cached, e.unmarshalError = unmarshalRecordPayload(e.PayloadType, e.RawPayload)
})
return e.cached, e.unmarshalError
}
// TypedRecord unmarshals the Envelope's payload to the given Record instance.
// It is the caller's responsibility to ensure that the Record type is capable
// of unmarshalling the Envelope payload. Callers can inspect the Envelope's
// PayloadType field to determine the correct type of Record to use.
//
// This method will always unmarshal the Envelope payload even if a cached record
// exists.
func (e *Envelope) TypedRecord(dest Record) error {
return dest.UnmarshalRecord(e.RawPayload)
}
// validate returns nil if the envelope signature is valid for the given 'domain',
// or an error if signature validation fails.
func (e *Envelope) validate(domain string) error {
unsigned, err := makeUnsigned(domain, e.PayloadType, e.RawPayload)
if err != nil {
return err
}
defer pool.Put(unsigned)
valid, err := e.PublicKey.Verify(unsigned, e.signature)
if err != nil {
return fmt.Errorf("failed while verifying signature: %w", err)
}
if !valid {
return ErrInvalidSignature
}
return nil
}
// makeUnsigned is a helper function that prepares a buffer to sign or verify.
// It returns a byte slice from a pool. The caller MUST return this slice to the
// pool.
func makeUnsigned(domain string, payloadType []byte, payload []byte) ([]byte, error) {
var (
fields = [][]byte{[]byte(domain), payloadType, payload}
// fields are prefixed with their length as an unsigned varint. we
// compute the lengths before allocating the sig buffer so we know how
// much space to add for the lengths
flen = make([][]byte, len(fields))
size = 0
)
for i, f := range fields {
l := len(f)
flen[i] = varint.ToUvarint(uint64(l))
size += l + len(flen[i])
}
b := pool.Get(size)
var s int
for i, f := range fields {
s += copy(b[s:], flen[i])
s += copy(b[s:], f)
}
return b[:s], nil
}

313
record/envelope_test.go Normal file
View File

@ -0,0 +1,313 @@
package record_test
import (
"bytes"
"errors"
"testing"
crypto "github.com/libp2p/go-libp2p-core/crypto"
. "github.com/libp2p/go-libp2p-core/record"
pb "github.com/libp2p/go-libp2p-core/record/pb"
"github.com/libp2p/go-libp2p-core/test"
"github.com/gogo/protobuf/proto"
)
type simpleRecord struct {
testDomain *string
testCodec []byte
message string
}
func (r *simpleRecord) Domain() string {
if r.testDomain != nil {
return *r.testDomain
}
return "libp2p-testing"
}
func (r *simpleRecord) Codec() []byte {
if r.testCodec != nil {
return r.testCodec
}
return []byte("/libp2p/testdata")
}
func (r *simpleRecord) MarshalRecord() ([]byte, error) {
return []byte(r.message), nil
}
func (r *simpleRecord) UnmarshalRecord(buf []byte) error {
r.message = string(buf)
return nil
}
// Make an envelope, verify & open it, marshal & unmarshal it
func TestEnvelopeHappyPath(t *testing.T) {
var (
rec = &simpleRecord{message: "hello world!"}
priv, pub, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
test.AssertNilError(t, err)
payload, err := rec.MarshalRecord()
test.AssertNilError(t, err)
envelope, err := Seal(rec, priv)
test.AssertNilError(t, err)
if !envelope.PublicKey.Equals(pub) {
t.Error("envelope has unexpected public key")
}
if bytes.Compare(rec.Codec(), envelope.PayloadType) != 0 {
t.Error("PayloadType does not match record Codec")
}
serialized, err := envelope.Marshal()
test.AssertNilError(t, err)
RegisterType(&simpleRecord{})
deserialized, rec2, err := ConsumeEnvelope(serialized, rec.Domain())
test.AssertNilError(t, err)
if bytes.Compare(deserialized.RawPayload, payload) != 0 {
t.Error("payload of envelope does not match input")
}
if !envelope.Equal(deserialized) {
t.Error("round-trip serde results in unequal envelope structures")
}
typedRec, ok := rec2.(*simpleRecord)
if !ok {
t.Error("expected ConsumeEnvelope to return record with type registered for payloadType")
}
if typedRec.message != "hello world!" {
t.Error("unexpected alteration of record")
}
}
func TestConsumeTypedEnvelope(t *testing.T) {
var (
rec = simpleRecord{message: "hello world!"}
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
envelope, err := Seal(&rec, priv)
test.AssertNilError(t, err)
envelopeBytes, err := envelope.Marshal()
test.AssertNilError(t, err)
rec2 := &simpleRecord{}
_, err = ConsumeTypedEnvelope(envelopeBytes, rec2)
test.AssertNilError(t, err)
if rec2.message != "hello world!" {
t.Error("unexpected alteration of record")
}
}
func TestMakeEnvelopeFailsWithEmptyDomain(t *testing.T) {
var (
rec = simpleRecord{message: "hello world!"}
domain = ""
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
if err != nil {
t.Fatal(err)
}
// override domain with empty string
rec.testDomain = &domain
_, err = Seal(&rec, priv)
test.ExpectError(t, err, "making an envelope with an empty domain should fail")
}
func TestMakeEnvelopeFailsWithEmptyPayloadType(t *testing.T) {
var (
rec = simpleRecord{message: "hello world!"}
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
if err != nil {
t.Fatal(err)
}
// override payload with empty slice
rec.testCodec = []byte{}
_, err = Seal(&rec, priv)
test.ExpectError(t, err, "making an envelope with an empty payloadType should fail")
}
type failingRecord struct {
allowMarshal bool
allowUnmarshal bool
}
func (r failingRecord) Domain() string {
return "testing"
}
func (r failingRecord) Codec() []byte {
return []byte("doesn't matter")
}
func (r failingRecord) MarshalRecord() ([]byte, error) {
if r.allowMarshal {
return []byte{}, nil
}
return nil, errors.New("marshal failed")
}
func (r failingRecord) UnmarshalRecord(data []byte) error {
if r.allowUnmarshal {
return nil
}
return errors.New("unmarshal failed")
}
func TestSealFailsIfRecordMarshalFails(t *testing.T) {
var (
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
if err != nil {
t.Fatal(err)
}
rec := failingRecord{}
_, err = Seal(rec, priv)
test.ExpectError(t, err, "Seal should fail if Record fails to marshal")
}
func TestConsumeEnvelopeFailsIfEnvelopeUnmarshalFails(t *testing.T) {
_, _, err := ConsumeEnvelope([]byte("not an Envelope protobuf"), "doesn't-matter")
test.ExpectError(t, err, "ConsumeEnvelope should fail if Envelope fails to unmarshal")
}
func TestConsumeEnvelopeFailsIfRecordUnmarshalFails(t *testing.T) {
var (
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
if err != nil {
t.Fatal(err)
}
RegisterType(failingRecord{})
rec := failingRecord{allowMarshal: true}
env, err := Seal(rec, priv)
test.AssertNilError(t, err)
envBytes, err := env.Marshal()
test.AssertNilError(t, err)
_, _, err = ConsumeEnvelope(envBytes, rec.Domain())
test.ExpectError(t, err, "ConsumeEnvelope should fail if Record fails to unmarshal")
}
func TestConsumeTypedEnvelopeFailsIfRecordUnmarshalFails(t *testing.T) {
var (
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
if err != nil {
t.Fatal(err)
}
RegisterType(failingRecord{})
rec := failingRecord{allowMarshal: true}
env, err := Seal(rec, priv)
test.AssertNilError(t, err)
envBytes, err := env.Marshal()
test.AssertNilError(t, err)
rec2 := failingRecord{}
_, err = ConsumeTypedEnvelope(envBytes, rec2)
test.ExpectError(t, err, "ConsumeTypedEnvelope should fail if Record fails to unmarshal")
}
func TestEnvelopeValidateFailsForDifferentDomain(t *testing.T) {
var (
rec = &simpleRecord{message: "hello world"}
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
test.AssertNilError(t, err)
envelope, err := Seal(rec, priv)
test.AssertNilError(t, err)
serialized, err := envelope.Marshal()
// try to open our modified envelope
_, _, err = ConsumeEnvelope(serialized, "wrong-domain")
test.ExpectError(t, err, "should not be able to open envelope with incorrect domain")
}
func TestEnvelopeValidateFailsIfPayloadTypeIsAltered(t *testing.T) {
var (
rec = &simpleRecord{message: "hello world!"}
domain = "libp2p-testing"
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
test.AssertNilError(t, err)
envelope, err := Seal(rec, priv)
test.AssertNilError(t, err)
serialized := alterMessageAndMarshal(t, envelope, func(msg *pb.Envelope) {
msg.PayloadType = []byte("foo")
})
// try to open our modified envelope
_, _, err = ConsumeEnvelope(serialized, domain)
test.ExpectError(t, err, "should not be able to open envelope with modified PayloadType")
}
func TestEnvelopeValidateFailsIfContentsAreAltered(t *testing.T) {
var (
rec = &simpleRecord{message: "hello world!"}
domain = "libp2p-testing"
priv, _, err = test.RandTestKeyPair(crypto.Ed25519, 256)
)
test.AssertNilError(t, err)
envelope, err := Seal(rec, priv)
test.AssertNilError(t, err)
serialized := alterMessageAndMarshal(t, envelope, func(msg *pb.Envelope) {
msg.Payload = []byte("totally legit, trust me")
})
// try to open our modified envelope
_, _, err = ConsumeEnvelope(serialized, domain)
test.ExpectError(t, err, "should not be able to open envelope with modified payload")
}
// Since we're outside of the crypto package (to avoid import cycles with test package),
// we can't alter the fields in a Envelope directly. This helper marshals
// the envelope to a protobuf and calls the alterMsg function, which should
// alter the protobuf message.
// Returns the serialized altered protobuf message.
func alterMessageAndMarshal(t *testing.T, envelope *Envelope, alterMsg func(*pb.Envelope)) []byte {
t.Helper()
serialized, err := envelope.Marshal()
test.AssertNilError(t, err)
msg := pb.Envelope{}
err = proto.Unmarshal(serialized, &msg)
test.AssertNilError(t, err)
alterMsg(&msg)
serialized, err = msg.Marshal()
test.AssertNilError(t, err)
return serialized
}

11
record/pb/Makefile Normal file
View File

@ -0,0 +1,11 @@
PB = $(wildcard *.proto)
GO = $(PB:.proto=.pb.go)
all: $(GO)
%.pb.go: %.proto
protoc --proto_path=$(PWD):$(PWD)/../.. --gogofaster_out=. $<
clean:
rm -f *.pb.go
rm -f *.go

504
record/pb/envelope.pb.go Normal file
View File

@ -0,0 +1,504 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: envelope.proto
package record_pb
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
pb "github.com/libp2p/go-libp2p-core/crypto/pb"
io "io"
math "math"
math_bits "math/bits"
)
// 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.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// Envelope encloses a signed payload produced by a peer, along with the public
// key of the keypair it was signed with so that it can be statelessly validated
// by the receiver.
//
// The payload is prefixed with a byte string that determines the type, so it
// can be deserialized deterministically. Often, this byte string is a
// multicodec.
type Envelope struct {
// public_key is the public key of the keypair the enclosed payload was
// signed with.
PublicKey *pb.PublicKey `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
// payload_type encodes the type of payload, so that it can be deserialized
// deterministically.
PayloadType []byte `protobuf:"bytes,2,opt,name=payload_type,json=payloadType,proto3" json:"payload_type,omitempty"`
// payload is the actual payload carried inside this envelope.
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
// signature is the signature produced by the private key corresponding to
// the enclosed public key, over the payload, prefixing a domain string for
// additional security.
Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"`
}
func (m *Envelope) Reset() { *m = Envelope{} }
func (m *Envelope) String() string { return proto.CompactTextString(m) }
func (*Envelope) ProtoMessage() {}
func (*Envelope) Descriptor() ([]byte, []int) {
return fileDescriptor_ee266e8c558e9dc5, []int{0}
}
func (m *Envelope) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Envelope) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Envelope.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Envelope) XXX_Merge(src proto.Message) {
xxx_messageInfo_Envelope.Merge(m, src)
}
func (m *Envelope) XXX_Size() int {
return m.Size()
}
func (m *Envelope) XXX_DiscardUnknown() {
xxx_messageInfo_Envelope.DiscardUnknown(m)
}
var xxx_messageInfo_Envelope proto.InternalMessageInfo
func (m *Envelope) GetPublicKey() *pb.PublicKey {
if m != nil {
return m.PublicKey
}
return nil
}
func (m *Envelope) GetPayloadType() []byte {
if m != nil {
return m.PayloadType
}
return nil
}
func (m *Envelope) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *Envelope) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func init() {
proto.RegisterType((*Envelope)(nil), "record.pb.Envelope")
}
func init() { proto.RegisterFile("envelope.proto", fileDescriptor_ee266e8c558e9dc5) }
var fileDescriptor_ee266e8c558e9dc5 = []byte{
// 205 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcd, 0x2b, 0x4b,
0xcd, 0xc9, 0x2f, 0x48, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x2c, 0x4a, 0x4d, 0xce,
0x2f, 0x4a, 0xd1, 0x2b, 0x48, 0x92, 0x12, 0x4b, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2f, 0x48,
0xd2, 0x87, 0xb0, 0x20, 0x4a, 0x94, 0x66, 0x31, 0x72, 0x71, 0xb8, 0x42, 0x75, 0x09, 0x19, 0x73,
0x71, 0x15, 0x94, 0x26, 0xe5, 0x64, 0x26, 0xc7, 0x67, 0xa7, 0x56, 0x4a, 0x30, 0x2a, 0x30, 0x6a,
0x70, 0x1b, 0x89, 0xe8, 0xc1, 0xd4, 0x27, 0xe9, 0x05, 0x80, 0x25, 0xbd, 0x53, 0x2b, 0x83, 0x38,
0x0b, 0x60, 0x4c, 0x21, 0x45, 0x2e, 0x9e, 0x82, 0xc4, 0xca, 0x9c, 0xfc, 0xc4, 0x94, 0xf8, 0x92,
0xca, 0x82, 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x6e, 0xa8, 0x58, 0x48, 0x65, 0x41,
0xaa, 0x90, 0x04, 0x17, 0x3b, 0x94, 0x2b, 0xc1, 0x0c, 0x96, 0x85, 0x71, 0x85, 0x64, 0xb8, 0x38,
0x8b, 0x33, 0xd3, 0xf3, 0x12, 0x4b, 0x4a, 0x8b, 0x52, 0x25, 0x58, 0xc1, 0x72, 0x08, 0x01, 0x27,
0x89, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63,
0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x48, 0x62, 0x03, 0xbb, 0xde, 0x18,
0x10, 0x00, 0x00, 0xff, 0xff, 0xaa, 0x0b, 0xd9, 0x6d, 0xf2, 0x00, 0x00, 0x00,
}
func (m *Envelope) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Envelope) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Envelope) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Signature) > 0 {
i -= len(m.Signature)
copy(dAtA[i:], m.Signature)
i = encodeVarintEnvelope(dAtA, i, uint64(len(m.Signature)))
i--
dAtA[i] = 0x2a
}
if len(m.Payload) > 0 {
i -= len(m.Payload)
copy(dAtA[i:], m.Payload)
i = encodeVarintEnvelope(dAtA, i, uint64(len(m.Payload)))
i--
dAtA[i] = 0x1a
}
if len(m.PayloadType) > 0 {
i -= len(m.PayloadType)
copy(dAtA[i:], m.PayloadType)
i = encodeVarintEnvelope(dAtA, i, uint64(len(m.PayloadType)))
i--
dAtA[i] = 0x12
}
if m.PublicKey != nil {
{
size, err := m.PublicKey.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintEnvelope(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintEnvelope(dAtA []byte, offset int, v uint64) int {
offset -= sovEnvelope(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *Envelope) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.PublicKey != nil {
l = m.PublicKey.Size()
n += 1 + l + sovEnvelope(uint64(l))
}
l = len(m.PayloadType)
if l > 0 {
n += 1 + l + sovEnvelope(uint64(l))
}
l = len(m.Payload)
if l > 0 {
n += 1 + l + sovEnvelope(uint64(l))
}
l = len(m.Signature)
if l > 0 {
n += 1 + l + sovEnvelope(uint64(l))
}
return n
}
func sovEnvelope(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozEnvelope(x uint64) (n int) {
return sovEnvelope(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Envelope) 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 ErrIntOverflowEnvelope
}
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: Envelope: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Envelope: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEnvelope
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthEnvelope
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthEnvelope
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.PublicKey == nil {
m.PublicKey = &pb.PublicKey{}
}
if err := m.PublicKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PayloadType", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEnvelope
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthEnvelope
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthEnvelope
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PayloadType = append(m.PayloadType[:0], dAtA[iNdEx:postIndex]...)
if m.PayloadType == nil {
m.PayloadType = []byte{}
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEnvelope
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthEnvelope
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthEnvelope
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Payload = append(m.Payload[:0], dAtA[iNdEx:postIndex]...)
if m.Payload == nil {
m.Payload = []byte{}
}
iNdEx = postIndex
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowEnvelope
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthEnvelope
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthEnvelope
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...)
if m.Signature == nil {
m.Signature = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipEnvelope(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthEnvelope
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthEnvelope
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipEnvelope(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEnvelope
}
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, ErrIntOverflowEnvelope
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowEnvelope
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthEnvelope
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupEnvelope
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthEnvelope
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthEnvelope = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowEnvelope = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupEnvelope = fmt.Errorf("proto: unexpected end of group")
)

30
record/pb/envelope.proto Normal file
View File

@ -0,0 +1,30 @@
syntax = "proto3";
package record.pb;
import "crypto/pb/crypto.proto";
// Envelope encloses a signed payload produced by a peer, along with the public
// key of the keypair it was signed with so that it can be statelessly validated
// by the receiver.
//
// The payload is prefixed with a byte string that determines the type, so it
// can be deserialized deterministically. Often, this byte string is a
// multicodec.
message Envelope {
// public_key is the public key of the keypair the enclosed payload was
// signed with.
crypto.pb.PublicKey public_key = 1;
// payload_type encodes the type of payload, so that it can be deserialized
// deterministically.
bytes payload_type = 2;
// payload is the actual payload carried inside this envelope.
bytes payload = 3;
// signature is the signature produced by the private key corresponding to
// the enclosed public key, over the payload, prefixing a domain string for
// additional security.
bytes signature = 5;
}

102
record/record.go Normal file
View File

@ -0,0 +1,102 @@
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
}

51
record/record_test.go Normal file
View File

@ -0,0 +1,51 @@
package record
import "testing"
var testPayloadType = []byte("/libp2p/test/record/payload-type")
type testPayload struct {
unmarshalPayloadCalled bool
}
func (p *testPayload) Domain() string {
return "testing"
}
func (p *testPayload) Codec() []byte {
return testPayloadType
}
func (p *testPayload) MarshalRecord() ([]byte, error) {
return []byte("hello"), nil
}
func (p *testPayload) UnmarshalRecord(bytes []byte) error {
p.unmarshalPayloadCalled = true
return nil
}
func TestUnmarshalPayload(t *testing.T) {
t.Run("fails if payload type is unregistered", func(t *testing.T) {
_, err := unmarshalRecordPayload([]byte("unknown type"), []byte{})
if err != ErrPayloadTypeNotRegistered {
t.Error("Expected error when unmarshalling payload with unregistered payload type")
}
})
t.Run("calls UnmarshalRecord on concrete Record type", func(t *testing.T) {
RegisterType(&testPayload{})
payload, err := unmarshalRecordPayload(testPayloadType, []byte{})
if err != nil {
t.Errorf("unexpected error unmarshalling registered payload type: %v", err)
}
typedPayload, ok := payload.(*testPayload)
if !ok {
t.Error("expected unmarshalled payload to be of the correct type")
}
if !typedPayload.unmarshalPayloadCalled {
t.Error("expected UnmarshalRecord to be called on concrete Record instance")
}
})
}

42
test/addrs.go Normal file
View File

@ -0,0 +1,42 @@
package test
import (
"fmt"
"testing"
ma "github.com/multiformats/go-multiaddr"
)
func GenerateTestAddrs(n int) []ma.Multiaddr {
out := make([]ma.Multiaddr, n)
for i := 0; i < n; i++ {
a, err := ma.NewMultiaddr(fmt.Sprintf("/ip4/1.2.3.4/tcp/%d", i))
if err != nil {
continue
}
out[i] = a
}
return out
}
func AssertAddressesEqual(t *testing.T, exp, act []ma.Multiaddr) {
t.Helper()
if len(exp) != len(act) {
t.Fatalf("lengths not the same. expected %d, got %d\n", len(exp), len(act))
}
for _, a := range exp {
found := false
for _, b := range act {
if a.Equal(b) {
found = true
break
}
}
if !found {
t.Fatalf("expected address %s not found", a)
}
}
}

19
test/errors.go Normal file
View File

@ -0,0 +1,19 @@
package test
import (
"testing"
)
func AssertNilError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func ExpectError(t *testing.T, err error, msg string) {
t.Helper()
if err == nil {
t.Error(msg)
}
}