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 `PssMsg`. 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/ethersphere/swarm/wiki/swarm-dev-progress.
Please report issues on https://github.com/ethersphere/Elastos.Geth
Feel free to ask questions in https://gitter.im/ethersphere/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 PssMsg 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 PssMsg 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 NewPingProtocol(ping *Ping) *p2p.Protocol
- func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error)
- 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 Topic, asymmetric bool, key string) (PssAddress, error)
- func (pssapi *API) GetAsymmetricAddressHint(topic Topic, pubkeyid string) (PssAddress, error)
- func (pssapi *API) GetPeerAddress(pubkeyhex string, topic Topic) (PssAddress, error)
- func (pssapi *API) GetPeerTopics(pubkeyhex string) ([]Topic, error)
- func (pssapi *API) GetPublicKey() (keybytes hexutil.Bytes)
- func (pssapi *API) GetSymmetricAddressHint(topic Topic, symkeyid string) (PssAddress, error)
- func (pssapi *API) GetSymmetricKey(symkeyid string) (hexutil.Bytes, error)
- func (pssapi *API) Receive(ctx context.Context, topic Topic) (*rpc.Subscription, error)
- func (pssapi *API) SendAsym(pubkeyhex string, topic Topic, msg hexutil.Bytes) error
- func (pssapi *API) SendSym(symkeyhex string, topic Topic, msg hexutil.Bytes) error
- func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic Topic, addr PssAddress) error
- func (pssapi *API) StringToTopic(topicstring string) (Topic, error)
- type APIMsg
- type Handler
- type HandshakeAPI
- func (api *HandshakeAPI) AddHandshake(topic Topic) error
- func (api *HandshakeAPI) GetHandshakeKeyCapacity(symkeyid string) (uint16, error)
- func (api *HandshakeAPI) GetHandshakeKeys(pubkeyid string, topic Topic, in bool, out bool) (keys []string, err error)
- func (api *HandshakeAPI) GetHandshakePublicKey(symkeyid string) (string, error)
- func (api *HandshakeAPI) Handshake(pubkeyid string, topic Topic, sync bool, flush bool) (keys []string, err error)
- func (api *HandshakeAPI) ReleaseHandshakeKey(pubkeyid string, topic Topic, symkeyid string, flush bool) (removed bool, err error)
- func (api *HandshakeAPI) RemoveHandshake(topic *Topic) error
- func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error)
- type HandshakeController
- type HandshakeParams
- 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) GenerateSymmetricKey(topic Topic, address *PssAddress, addToCache bool) (string, error)
- func (p *Pss) GetPublickeyPeers(keyid string) (topic []Topic, address []PssAddress, err error)
- func (p *Pss) GetSymmetricKey(symkeyid string) ([]byte, error)
- func (p *Pss) Protocols() []p2p.Protocol
- func (p *Pss) PublicKey() *ecdsa.PublicKey
- func (p *Pss) Register(topic *Topic, handler Handler) func()
- func (p *Pss) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error
- func (p *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error
- func (p *Pss) SendRaw(address PssAddress, topic Topic, msg []byte) error
- func (p *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error
- func (p *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address *PssAddress) error
- func (p *Pss) SetSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool) (string, error)
- func (p *Pss) Start(srv *p2p.Server) error
- func (p *Pss) Stop() error
- func (p *Pss) String() string
- type PssAddress
- type PssMsg
- type PssParams
- type PssReadWriter
- type Topic
Constants ¶
const (
DefaultMsgTTL = time.Second * 120
)
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 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 (pssapi *API) GetAsymmetricAddressHint(topic Topic, pubkeyid string) (PssAddress, error)
func (*API) GetPeerAddress ¶
func (pssapi *API) GetPeerAddress(pubkeyhex string, topic Topic) (PssAddress, error)
func (*API) GetPublicKey ¶
Retrieves the node's public key in hex form
func (*API) GetSymmetricAddressHint ¶
func (pssapi *API) GetSymmetricAddressHint(topic Topic, symkeyid string) (PssAddress, error)
func (*API) GetSymmetricKey ¶
func (*API) Receive ¶
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 ¶
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 Handler ¶
Signature for a message handler function for a PssMsg
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 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 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 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 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 *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 Protocol ¶
type Protocol struct { *Pss Asymmetric bool Symmetric bool RWPoolMu sync.Mutex // contains filtered or unexported fields }
Convenience object for emulation devp2p over pss
func RegisterProtocol ¶
func RegisterProtocol(ps *Pss, topic *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 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 // contains filtered or unexported fields }
Toplevel pss object, takes care of message sending, receiving, decryption and encryption, message handler dispatchers and message forwarding.
Implements node.Service
func NewPss ¶
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) GenerateSymmetricKey ¶
func (p *Pss) GenerateSymmetricKey(topic Topic, address *PssAddress, addToCache bool) (string, error)
Automatically generate a new symkey for a topic and address hint
func (*Pss) GetPublickeyPeers ¶
func (p *Pss) GetPublickeyPeers(keyid string) (topic []Topic, address []PssAddress, err error)
Returns all recorded topic and address combination for a specific public key
func (*Pss) GetSymmetricKey ¶
Returns a symmetric key byte seqyence stored in the whisper backend by its unique id
Passes on the error value from the whisper backend
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 Topic, msg []byte) error
Send a raw message (any encryption is responsibility of calling client)
Will fail if raw messages are disallowed
func (*Pss) SendSym ¶
Send a message using symmetric encryption
Fails if the key id does not match any of the stored symmetric keys
func (*Pss) SetPeerPublicKey ¶
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 (*Pss) SetSymmetricKey ¶
func (p *Pss) SetSymmetricKey(key []byte, topic 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 whisper 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 whisper backend (see pss.GetSymmetricKey())
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 PssParams ¶
type PssParams struct { MsgTTL time.Duration CacheTTL time.Duration SymKeyCacheCapacity int AllowRaw bool // If true, enables sending and receiving messages without builtin pss encryption // contains filtered or unexported fields }
Pss configuration parameters
func (*PssParams) WithPrivateKey ¶
func (params *PssParams) WithPrivateKey(privatekey *ecdsa.PrivateKey) *PssParams
type PssReadWriter ¶
PssReadWriter bridges pss send/receive with devp2p protocol send/receive
Implements p2p.MsgReadWriter
type Topic ¶
Topic is the PSS encapsulation of the Whisper topic type
func BytesToTopic ¶
BytesToTopic hashes an arbitrary length byte slice and truncates it to the length of a topic, using only the first bytes of the digest
func ProtocolTopic ¶
Uniform translation of protocol specifiers to topic
func (Topic) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface
func (*Topic) UnmarshalJSON ¶
MarshalJSON implements the json.Marshaler interface
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/wuyazero/Elastos.Geth/swarm/pss/client" "github.com/wuyazero/Elastos.Geth/p2p/protocols" "github.com/wuyazero/Elastos.Geth/p2p" "github.com/wuyazero/Elastos.Geth/swarm/pot" "github.com/wuyazero/Elastos.Geth/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.RandomAddress() // 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/wuyazero/Elastos.Geth/swarm/pss/client" "github.com/wuyazero/Elastos.Geth/p2p/protocols" "github.com/wuyazero/Elastos.Geth/p2p" "github.com/wuyazero/Elastos.Geth/swarm/pot" "github.com/wuyazero/Elastos.Geth/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.RandomAddress() // 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 |