Documentation ¶
Overview ¶
Pss provides devp2p functionality for swarm nodes without the need for a direct tcp connection between them.
Messages are encapsulated in a devp2p message structure `message.Message`. These capsules are forwarded from node to node using ordinary tcp devp2p until it reaches its destination: The node or nodes who can successfully decrypt the message.
Routing of messages is done using swarm's own kademlia routing. Optionally routing can be turned off, forcing the message to be sent to all peers, similar to the behavior of the whisper protocol.
Pss is intended for messages of limited size, typically a couple of Kbytes at most. The messages themselves can be anything at all; complex data structures or non-descript byte sequences.
Documentation can be found in the README file.
For the current state and roadmap of pss development please see https://github.com/holisticode/swarm/wiki/swarm-dev-progress.
Please report issues on https://github.com/holisticode/go-ethereum
Feel free to ask questions in https://gitter.im/holisticode/pss
TOPICS ¶
An encrypted envelope of a pss messages always contains a Topic. This is pss' way of determining what action to take on the message. The topic is only visible for the node(s) who can decrypt the message.
This "topic" is not like the subject of an email message, but a hash-like arbitrary 4 byte value. A valid topic can be generated using the `pss_*ToTopic` API methods.
IDENTITY IN PSS ¶
Pss aims to achieve perfect darkness. That means that the minimum requirement for two nodes to communicate using pss is a shared secret. This secret can be an arbitrary byte slice, or a ECDSA keypair.
Peer keys can manually be added to the pss node through its API calls `pss_setPeerPublicKey` and `pss_setSymmetricKey`. Keys are always coupled with a topic, and the keys will only be valid for these topics.
CONNECTIONS ¶
A "connection" in pss is a purely virtual construct. There is no mechanisms in place to ensure that the remote peer actually is there. In fact, "adding" a peer involves merely the node's opinion that the peer is there. It may issue messages to that remote peer to a directly connected peer, which in turn passes it on. But if it is not present on the network - or if there is no route to it - the message will never reach its destination through mere forwarding.
When implementing the devp2p protocol stack, the "adding" of a remote peer is a prerequisite for the side actually initiating the protocol communication. Adding a peer in effect "runs" the protocol on that peer, and adds an internal mapping between a topic and that peer. It also enables sending and receiving messages using the main io-construct in devp2p - the p2p.MsgReadWriter.
Under the hood, pss implements its own MsgReadWriter, which bridges MsgReadWriter.WriteMsg with Pss.SendRaw, and deftly adds an InjectMsg method which pipes incoming messages to appear on the MsgReadWriter.ReadMsg channel.
An incoming connection is nothing more than an actual message.Message appearing with a certain Topic. If a Handler har been registered to that Topic, the message will be passed to it. This constitutes a "new" connection if:
- The pss node never called AddPeer with this combination of remote peer address and topic, and
- The pss node never received a message.Message from this remote peer with this specific Topic before.
If it is a "new" connection, the protocol will be "run" on the remote peer, in the same manner as if it was pre-emptively added.
Index ¶
- Constants
- Variables
- func NewHandler(f HandlerFunc) *handler
- func NewPingProtocol(ping *Ping) *p2p.Protocol
- func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error)
- func ProtocolTopic(spec *protocols.Spec) message.Topic
- func SetHandshakeController(pss *Pss, params *HandshakeParams) error
- func ToP2pMsg(msg []byte) (p2p.Msg, error)
- type API
- func (pssapi *API) BaseAddr() (PssAddress, error)
- func (pssapi *API) GetAddress(topic message.Topic, asymmetric bool, key string) (PssAddress, error)
- func (pssapi *API) GetAsymmetricAddressHint(topic message.Topic, pubkeyid string) (PssAddress, error)
- func (pssapi *API) GetPeerAddress(pubkeyhex string, topic message.Topic) (PssAddress, error)
- func (pssapi *API) GetPeerTopics(pubkeyhex string) ([]message.Topic, error)
- func (pssapi *API) GetPublicKey() (keybytes hexutil.Bytes)
- func (pssapi *API) GetSymmetricAddressHint(topic message.Topic, symkeyid string) (PssAddress, error)
- func (pssapi *API) GetSymmetricKey(symkeyid string) (hexutil.Bytes, error)
- func (pssapi *API) Receive(ctx context.Context, topic message.Topic, raw bool, prox bool) (*rpc.Subscription, error)
- func (pssapi *API) SendAsym(pubkeyhex string, topic message.Topic, msg hexutil.Bytes) error
- func (pssapi *API) SendRaw(addr hexutil.Bytes, topic message.Topic, msg hexutil.Bytes) error
- func (pssapi *API) SendSym(symkeyhex string, topic message.Topic, msg hexutil.Bytes) error
- func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic message.Topic, addr PssAddress) error
- func (pssapi *API) StringToTopic(topicstring string) (message.Topic, error)
- type APIMsg
- type HandlerFunc
- type HandshakeAPI
- func (api *HandshakeAPI) AddHandshake(topic message.Topic) error
- func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error)
- func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic message.Topic, in bool, out bool) (keys []string, err error)
- func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error)
- func (api *HandshakeAPI) Handshake(pubkeyid string, topic message.Topic, sync bool, flush bool) (keys []string, err error)
- func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic message.Topic, symkeyid string, flush bool) (removed bool, err error)
- func (api *HandshakeAPI) RemoveHandshake(topic *message.Topic) error
- func (api *HandshakeAPI) SendSym(symkeyid string, topic message.Topic, msg hexutil.Bytes) (err error)
- type HandshakeController
- type HandshakeParams
- type KeyStore
- func (ks *KeyStore) GenerateSymmetricKey(topic message.Topic, address PssAddress, addToCache bool) (string, error)
- func (ks *KeyStore) GetPublickeyPeers(keyid string) (topic []message.Topic, address []PssAddress, err error)
- func (ks *KeyStore) GetSymmetricKey(symkeyid string) ([]byte, error)
- func (ks *KeyStore) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic message.Topic, address PssAddress) error
- func (ks *KeyStore) SetSymmetricKey(key []byte, topic message.Topic, address PssAddress, addtocache bool) (string, error)
- type Params
- type Ping
- type PingMsg
- type Protocol
- type ProtocolMsg
- type ProtocolParams
- type Pss
- func (p *Pss) APIs() []rpc.API
- func (p *Pss) BaseAddr() []byte
- func (p *Pss) Protocols() []p2p.Protocol
- func (p *Pss) PublicKey() *ecdsa.PublicKey
- func (p *Pss) Register(topic *message.Topic, hndlr *handler) func()
- func (p *Pss) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error
- func (p *Pss) SendAsym(pubkeyid string, topic message.Topic, msg []byte) error
- func (p *Pss) SendRaw(address PssAddress, topic message.Topic, msg []byte, messageTTL time.Duration) error
- func (p *Pss) SendSym(symkeyid string, topic message.Topic, msg []byte) error
- func (p *Pss) Start(srv *p2p.Server) error
- func (p *Pss) Stop() error
- func (p *Pss) String() string
- type PssAddress
- type PssReadWriter
- type PubSub
Constants ¶
const (
CapabilityID = capability.CapabilityID(1)
)
const (
IsActiveHandshake = true
)
const (
IsActiveProtocol = true
)
Variables ¶
var PingProtocol = &protocols.Spec{ Name: "psstest", Version: 1, MaxMsgSize: 1024, Messages: []interface{}{ PingMsg{}, }, }
var PingTopic = ProtocolTopic(PingProtocol)
Functions ¶
func NewPingProtocol ¶
func NewProtocolMsg ¶
Creates a ProtocolMsg
func ProtocolTopic ¶
Uniform translation of protocol specifiers to topic
func SetHandshakeController ¶
func SetHandshakeController(pss *Pss, params *HandshakeParams) error
Attach HandshakeController to pss node
Must be called before starting the pss node service
Types ¶
type API ¶
type API struct {
*Pss
}
Additional public methods accessible through API for pss
func (*API) BaseAddr ¶
func (pssapi *API) BaseAddr() (PssAddress, error)
Retrieves the node's base address in hex form
func (*API) GetAddress ¶
func (*API) GetAsymmetricAddressHint ¶
func (*API) GetPeerAddress ¶
func (*API) GetPeerTopics ¶
func (*API) GetPublicKey ¶
Retrieves the node's public key in hex form
func (*API) GetSymmetricAddressHint ¶
func (*API) GetSymmetricKey ¶
func (*API) Receive ¶
func (pssapi *API) Receive(ctx context.Context, topic message.Topic, raw bool, prox bool) (*rpc.Subscription, error)
Creates a new subscription for the caller. Enables external handling of incoming messages.
A new handler is registered in pss for the supplied topic ¶
All incoming messages to the node matching this topic will be encapsulated in the APIMsg struct and sent to the subscriber
func (*API) SetPeerPublicKey ¶
func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic message.Topic, addr PssAddress) error
Set Public key to associate with a particular Pss peer
type APIMsg ¶
Wrapper for receiving pss messages when using the pss API providing access to sender of message
type HandlerFunc ¶
Signature for a message handler function for a Message Implementations of this type are passed to Pss.Register together with a topic,
type HandshakeAPI ¶
type HandshakeAPI struct {
// contains filtered or unexported fields
}
func (*HandshakeAPI) AddHandshake ¶
func (api *HandshakeAPI) AddHandshake(topic message.Topic) error
Activate handshake functionality on a topic
func (*HandshakeAPI) GetHandshakeKeyCapacity ¶
func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error)
Returns the amount of messages the specified symmetric key is still valid for under the handshake scheme
func (*HandshakeAPI) GetHandshakeKeys ¶
func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic message.Topic, in bool, out bool) (keys []string, err error)
Returns all valid symmetric keys in store per peer (public key) and topic.
The `in` and `out` parameters indicate for which direction(s) symmetric keys will be returned. If both are false, no keys (and no error) will be returned.
func (*HandshakeAPI) GetHandshakePublicKey ¶
func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error)
Returns the byte representation of the public key in ascii hex associated with the given symmetric key
func (*HandshakeAPI) Handshake ¶
func (api *HandshakeAPI) Handshake(pubkeyid string, topic message.Topic, sync bool, flush bool) (keys []string, err error)
Initiate a handshake session for a peer (public key) and topic combination.
If `sync` is set, the call will block until keys are received from peer, or if the handshake request times out
If `flush` is set, the max amount of keys will be sent to the peer regardless of how many valid keys that currently exist in the store.
Returns list of symmetric key ids that can be passed to pss.GetSymmetricKey() for retrieval of the symmetric key bytes themselves.
Fails if the incoming symmetric key store is already full (and `flush` is false), or if the underlying key dispatcher fails
func (*HandshakeAPI) ReleaseHandshakeKey ¶
func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic message.Topic, symkeyid string, flush bool) (removed bool, err error)
Manually expire the given symkey
If `flush` is set, garbage collection will be performed before returning.
Returns true on successful removal, false otherwise
func (*HandshakeAPI) RemoveHandshake ¶
func (api *HandshakeAPI) RemoveHandshake(topic *message.Topic) error
Deactivate handshake functionality on a topic
type HandshakeController ¶
type HandshakeController struct {
// contains filtered or unexported fields
}
Singleton object enabling semi-automatic Diffie-Hellman exchange of ephemeral symmetric keys
type HandshakeParams ¶
type HandshakeParams struct { SymKeyRequestTimeout time.Duration SymKeyExpiryTimeout time.Duration SymKeySendLimit uint16 SymKeyCapacity uint8 }
Initialization parameters for the HandshakeController
SymKeyRequestExpiry: Timeout for waiting for a handshake reply (default 8000 ms)
SymKeySendLimit: Amount of messages symmetric keys issues by this node is valid for (default 256)
SymKeyCapacity: Ideal (and maximum) amount of symmetric keys held per direction per peer (default 4)
func NewHandshakeParams ¶
func NewHandshakeParams() *HandshakeParams
Sane defaults for HandshakeController initialization
type KeyStore ¶
type KeyStore struct { Crypto crypto.Crypto // key and encryption crypto // contains filtered or unexported fields }
func (*KeyStore) GenerateSymmetricKey ¶
func (ks *KeyStore) GenerateSymmetricKey(topic message.Topic, address PssAddress, addToCache bool) (string, error)
Automatically generate a new symkey for a topic and address hint
func (*KeyStore) GetPublickeyPeers ¶
func (ks *KeyStore) GetPublickeyPeers(keyid string) (topic []message.Topic, address []PssAddress, err error)
Returns all recorded topic and address combination for a specific public key
func (*KeyStore) GetSymmetricKey ¶
Returns a symmetric key byte sequence stored in the crypto backend by its unique id. Passes on the error value from the crypto backend.
func (*KeyStore) SetPeerPublicKey ¶
func (ks *KeyStore) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic message.Topic, address PssAddress) error
Links a peer ECDSA public key to a topic. This is required for asymmetric message exchange on the given topic. The value in `address` will be used as a routing hint for the public key / topic association.
func (*KeyStore) SetSymmetricKey ¶
func (ks *KeyStore) SetSymmetricKey(key []byte, topic message.Topic, address PssAddress, addtocache bool) (string, error)
Links a peer symmetric key (arbitrary byte sequence) to a topic.
This is required for symmetrically encrypted message exchange on the given topic.
The key is stored in the crypto backend.
If addtocache is set to true, the key will be added to the cache of keys used to attempt symmetric decryption of incoming messages.
Returns a string id that can be used to retrieve the key bytes from the crypto backend (see pss.GetSymmetricKey())
type Params ¶
type Params struct { MsgTTL time.Duration CacheTTL time.Duration SymKeyCacheCapacity int AllowRaw bool // If true, enables sending and receiving messages without builtin pss encryption AllowForward bool // contains filtered or unexported fields }
Pss configuration parameters
func (*Params) WithPrivateKey ¶
func (params *Params) WithPrivateKey(privatekey *ecdsa.PrivateKey) *Params
type Protocol ¶
type Protocol struct { *Pss Asymmetric bool Symmetric bool // contains filtered or unexported fields }
Convenience object for emulation devp2p over pss
func RegisterProtocol ¶
func RegisterProtocol(ps *Pss, topic *message.Topic, spec *protocols.Spec, targetprotocol *p2p.Protocol, options *ProtocolParams) (*Protocol, error)
Activates devp2p emulation over a specific pss topic
One or both encryption schemes must be specified. If only one is specified, the protocol will not be valid for the other, and will make the message handler return errors
func (*Protocol) AddPeer ¶
func (p *Protocol) AddPeer(peer *p2p.Peer, topic message.Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error)
Runs an emulated pss Protocol on the specified peer, linked to a specific topic `key` and `asymmetric` specifies what encryption key to link the peer to. The key must exist in the pss store prior to adding the peer.
func (*Protocol) Handle ¶
Generic handler for incoming messages over devp2p emulation
To be passed to pss.Register()
Will run the protocol on a new incoming peer, provided that the encryption key of the message has a match in the internal pss keypool
Fails if protocol is not valid for the message encryption scheme, if adding a new peer fails, or if the message is not a serialized p2p.Msg (which it always will be if it is sent from this object).
func (*Protocol) RemovePeer ¶
type ProtocolMsg ¶
Convenience wrapper for devp2p protocol messages for transport over pss
type ProtocolParams ¶
Protocol options to be passed to a new Protocol instance
The parameters specify which encryption schemes to allow
type Pss ¶
type Pss struct { *network.Kademlia // we can get the Kademlia address from this *KeyStore // contains filtered or unexported fields }
Pss is the top-level struct, which takes care of message sending, receiving, decryption and encryption, message handler dispatchers and message forwarding. Implements node.Service
func New ¶
Creates a new Pss instance.
In addition to params, it takes a swarm network Kademlia and a FileStore storage for message cache storage.
func (*Pss) Register ¶
Links a handler function to a Topic
All incoming messages with an envelope Topic matching the topic specified will be passed to the given Handler function.
There may be an arbitrary number of handler functions per topic.
Returns a deregister function which needs to be called to deregister the handler,
func (*Pss) SendAsym ¶
Send a message using asymmetric encryption
Fails if the key id does not match any in of the stored public keys
func (*Pss) SendRaw ¶
func (p *Pss) SendRaw(address PssAddress, topic message.Topic, msg []byte, messageTTL time.Duration) error
Send a raw message (any encryption is responsibility of calling client)
Will fail if raw messages are disallowed
type PssAddress ¶
type PssAddress []byte
PssAddress is an alias for []byte. It represents a variable length address
func (PssAddress) MarshalJSON ¶
func (a PssAddress) MarshalJSON() ([]byte, error)
MarshalJSON implements the json.Marshaler interface
func (*PssAddress) UnmarshalJSON ¶
func (a *PssAddress) UnmarshalJSON(input []byte) error
UnmarshalJSON implements the json.Marshaler interface
type PssReadWriter ¶
PssReadWriter bridges pss send/receive with devp2p protocol send/receive
Implements p2p.MsgReadWriter
type PubSub ¶
type PubSub struct {
// contains filtered or unexported fields
}
PubSub implements the pushsync.PubSub interface using pss
func (*PubSub) IsClosestTo ¶
IsClosestTo returns true is self is the closest known node to addr as uniquely defined by the MSB XOR distance among pss capable peers
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
simple abstraction for implementing pss functionality the pss client library aims to simplify usage of the p2p.protocols package over pss IO is performed using the ordinary p2p.MsgReadWriter interface, which transparently communicates with a pss node via RPC using websockets as transport layer, using methods in the PssAPI class in the swarm/pss package Minimal-ish usage example (requires a running pss node with websocket RPC): import ( "context" "fmt" "os" pss "github.com/holisticode/swarm/pss/client" "github.com/holisticode/swarm/p2p/protocols" "github.com/ethereum/go-ethereum/p2p" "github.com/holisticode/swarm/pot" "github.com/holisticode/swarm/log" ) type FooMsg struct { Bar int } func fooHandler (msg interface{}) error { foomsg, ok := msg.(*FooMsg) if ok { log.Debug("Yay, just got a message", "msg", foomsg) } return errors.New(fmt.Sprintf("Unknown message")) } spec := &protocols.Spec{ Name: "foo", Version: 1, MaxMsgSize: 1024, Messages: []interface{}{ FooMsg{}, }, } proto := &p2p.Protocol{ Name: spec.Name, Version: spec.Version, Length: uint64(len(spec.Messages)), Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { pp := protocols.NewPeer(p, rw, spec) return pp.Run(fooHandler) }, } func implementation() { cfg := pss.NewClientConfig() psc := pss.NewClient(context.Background(), nil, cfg) err := psc.Start() if err != nil { log.Crit("can't start pss client") os.Exit(1) } log.Debug("connected to pss node", "bzz addr", psc.BaseAddr) err = psc.RunProtocol(proto) if err != nil { log.Crit("can't start protocol on pss websocket") os.Exit(1) } addr := pot.RandomBzzAddress() // should be a real address, of course psc.AddPssPeer(addr, spec) // use the protocol for something psc.Stop() } BUG(test): TestIncoming test times out due to deadlock issues in the swarm hive
|
simple abstraction for implementing pss functionality the pss client library aims to simplify usage of the p2p.protocols package over pss IO is performed using the ordinary p2p.MsgReadWriter interface, which transparently communicates with a pss node via RPC using websockets as transport layer, using methods in the PssAPI class in the swarm/pss package Minimal-ish usage example (requires a running pss node with websocket RPC): import ( "context" "fmt" "os" pss "github.com/holisticode/swarm/pss/client" "github.com/holisticode/swarm/p2p/protocols" "github.com/ethereum/go-ethereum/p2p" "github.com/holisticode/swarm/pot" "github.com/holisticode/swarm/log" ) type FooMsg struct { Bar int } func fooHandler (msg interface{}) error { foomsg, ok := msg.(*FooMsg) if ok { log.Debug("Yay, just got a message", "msg", foomsg) } return errors.New(fmt.Sprintf("Unknown message")) } spec := &protocols.Spec{ Name: "foo", Version: 1, MaxMsgSize: 1024, Messages: []interface{}{ FooMsg{}, }, } proto := &p2p.Protocol{ Name: spec.Name, Version: spec.Version, Length: uint64(len(spec.Messages)), Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { pp := protocols.NewPeer(p, rw, spec) return pp.Run(fooHandler) }, } func implementation() { cfg := pss.NewClientConfig() psc := pss.NewClient(context.Background(), nil, cfg) err := psc.Start() if err != nil { log.Crit("can't start pss client") os.Exit(1) } log.Debug("connected to pss node", "bzz addr", psc.BaseAddr) err = psc.RunProtocol(proto) if err != nil { log.Crit("can't start protocol on pss websocket") os.Exit(1) } addr := pot.RandomBzzAddress() // should be a real address, of course psc.AddPssPeer(addr, spec) // use the protocol for something psc.Stop() } BUG(test): TestIncoming test times out due to deadlock issues in the swarm hive |
internal
|
|