pss

package
v0.0.0-...-0e99d01 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2024 License: GPL-3.0 Imports: 23 Imported by: 0

README

Postal Services over Swarm

pss enables message relay over swarm. This means nodes can send messages to each other without being directly connected with each other, while taking advantage of the efficient routing algorithms that swarm uses for transporting and storing data.

CONTENTS
  • Status of this document
  • Core concepts
  • Caveat
  • Examples
  • API
    • Retrieve node information
    • Receive messages
    • Send messages using public key encryption
    • Send messages using symmetric encryption
    • Querying peer keys
    • Handshakes
STATUS OF THIS DOCUMENT

pss is under active development, and the first implementation is yet to be merged to the Ethereum main branch. Expect things to change.

Details on swarm routing and encryption schemes out of scope of this document.

Please refer to ARCHITECTURE.md for in-depth topics concerning pss.

CORE CONCEPTS

Three things are required to send a pss message:

  1. Encryption key
  2. Topic
  3. Message payload

Encryption key can be a public key or a 32 byte symmetric key. It must be coupled with a peer address in the node prior to sending.

Topic is the initial 4 bytes of a hash value.

Message payload is an arbitrary byte slice of data.

Upon sending the message it is encrypted and passed on from peer to peer. Any node along the route that can successfully decrypt the message is regarded as a recipient. Recipients continue to pass on the message to their peers, to make traffic analysis attacks more difficult.

The Address that is coupled with the encryption keys are used for routing the message. This does not need to be a full addresses; the network will route the message to the best of its ability with the information that is available. If no address is given (zero-length byte slice), routing is effectively deactivated, and the message is passed to all peers by all peers.

CAVEAT

pss connectivity resembles UDP. This means there is no delivery guarantee for a message. Furthermore there is no strict definition of what a connection between two nodes communicating via pss is. Reception acknowledgements and keepalive-schemes is the responsibility of the application.

Due to the inherent properties of the swarm routing algorithm, a node may receive the same message more than once. Message deduplication cannot be guaranteed by pss, and must be handled in the application layer to ensure predictable results.

EXAMPLES

The code tutorial p2p programming in go-ethereum by @nolash provides step-by-step code examples for usage of pss API with go-ethereum nodes.

A quite unpolished example using javascript is available here: https://github.com/nolash/pss-js/tree/withcrypt

API

The pss API is available through IPC and Websockets. There is currently no web3.js implementation, as this does not support message subscription.

For golang clients, please use the rpc.Client provided by the go-ethereum repository. The return values may have special types in golang. Please refer to godoc for details.

RETRIEVE NODE INFORMATION
pss_getPublicKey

Retrieves the public key of the node, in hex format

parameters:
none

returns:
1. publickey (hex)
pss_baseAddr

Retrieves the swarm overlay address of the node, in hex format

parameters:
none

returns:
1. swarm overlay address (hex)
pss_stringToTopic

Creates a deterministic 4 byte topic value from input, returned in hex format

parameters:
1. topic string (string)

returns:
1. pss topic (hex)
RECEIVE MESSAGES
pss_subscribe

Creates a subscription. Received messages with matching topic will be passed to subscription client.

parameters:
1. string("receive")
2. topic (4 bytes in hex)

returns:
1. subscription handle `base64(byte)` `rpc.ClientSubscription`

In golang as special method is used:

rpc.Client.Subscribe(context.Context, "pss", chan pss.APIMsg, "receive", pss.Topic)

Incoming messages are encapsulated in an object (pss.APIMsg in golang) with the following members:

1. Msg (hex) - the message payload
2. Asymmetric (bool) - true if message used public key encryption
3. Key (string) - the encryption key used
SEND MESSAGE USING PUBLIC KEY ENCRYPTION
pss_setPeerPublicKey

Register a peer's public key. This is done once for every topic that will be used with the peer. Address can be anything from 0 to 32 bytes inclusive of the peer's swarm overlay address.

parameters:
1. public key of peer (hex)
2. topic (4 bytes in hex)
3. address of peer (hex)

returns:
none
pss_sendAsym

Encrypts the message using the provided public key, and signs it using the node's private key. It then wraps it in an envelope containing the topic, and sends it to the network.

parameters:
1. public key of peer (hex)
2. topic (4 bytes in hex)
3. message (hex)

returns:
none
SEND MESSAGE USING SYMMETRIC ENCRYPTION
pss_setSymmetricKey

Register a symmetric key shared with a peer. This is done once for every topic that will be used with the peer. Address can be anything from 0 to 32 bytes inclusive of the peer's swarm overlay address.

If the fourth parameter is false, the key will not be added to the list of symmetric keys used for decryption attempts.

parameters:
1. symmetric key (hex)
2. topic (4 bytes in hex)
3. address of peer (hex)
4. use for decryption (bool)

returns:
1. symmetric key id (string)
pss_sendSym

Encrypts the message using the provided symmetric key, wraps it in an envelope containing the topic, and sends it to the network.

parameters:
1. symmetric key id (string)
2. topic (4 bytes in hex)
3. message (hex)

returns:
none
QUERY PEER KEYS
pss_GetSymmetricAddressHint

Return the swarm overlay address associated with the peer registered with the given symmetric key and topic combination.

parameters:
1. topic (4 bytes in hex)
2. symmetric key id (string)

returns:
1. peer address (hex)
pss_GetAsymmetricAddressHint

Return the swarm overlay address associated with the peer registered with the given symmetric key and topic combination.

parameters:
1. topic (4 bytes in hex)
2. public key in hex form (string)

returns:
1. peer address (hex)
HANDSHAKES

Convenience implementation of Diffie-Hellman handshakes using ephemeral symmetric keys. Peers keep separate sets of keys for incoming and outgoing communications.

This functionality is an optional feature in pss. It is compiled in by default, but can be omitted by providing the nopsshandshake build tag.

pss_addHandshake

Activate handshake functionality on the specified topic.

parameters:
1. topic (4 bytes in hex)

returns:
none
pss_removeHandshake

Remove handshake functionality on the specified topic.

parameters:
1. topic (4 bytes in hex)

returns:
none
pss_handshake

Instantiate handshake with peer, refreshing symmetric encryption keys.

If parameter 3 is false, the returned array will be empty.

parameters:
1. public key of peer in hex format (string)
2. topic (4 bytes in hex)
3. block calls until keys are received (bool)
4. flush existing incoming keys (bool)

returns:
1. list of symmetric keys (string[])
pss_getHandshakeKeys

Get valid symmetric encryption keys for a specified peer and topic.

parameters:

  1. public key of peer in hex format (string)
  2. topic (4 bytes in hex)
  3. include keys for incoming messages (bool)
  4. include keys for outgoing messages (bool)

returns:

  1. list of symmetric keys (string[])
pss_getHandshakeKeyCapacity

Get amount of remaining messages the specified key is valid for.

parameters:
1. symmetric key id (string)

returns:
1. number of messages (uint16)
pss_getHandshakePublicKey

Get the peer's public key associated with the specified symmetric key.

parameters:
1. symmetric key id (string)

returns:
1. Associated public key in hex format (string)
pss_releaseHandshakeKey

Invalidate the specified key.

Normally, the key will be kept for a grace period to allow for decryption of delayed messages. If instant removal is set, this grace period is omitted, and the key removed instantaneously.

parameters:
1. public key of peer in hex format (string)
2. topic (4 bytes in hex)
3. symmetric key id to release (string)
4. remove keys instantly (bool)

returns:
1. whether key was successfully removed (bool)

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/go-ethereum

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

View Source
const (
	IsActiveHandshake = true
)
View Source
const (
	IsActiveProtocol = true
)

Variables

View Source
var PingProtocol = &protocols.Spec{
	Name:       "psstest",
	Version:    1,
	MaxMsgSize: 1024,
	Messages: []interface{}{
		PingMsg{},
	},
}

Functions

func NewPingProtocol

func NewPingProtocol(ping *Ping) *p2p.Protocol

func NewProtocolMsg

func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error)

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

func ToP2pMsg

func ToP2pMsg(msg []byte) (p2p.Msg, error)

Creates a serialized (non-buffered) version of a p2p.Msg, used in the specialized internal p2p.MsgReadwriter implementations

Types

type API

type API struct {
	*Pss
}

Additional public methods accessible through API for pss

func NewAPI

func NewAPI(ps *Pss) *API

func (*API) BaseAddr

func (pssapi *API) BaseAddr() (PssAddress, error)

Retrieves the node's base address in hex form

func (*API) GetAddress

func (pssapi *API) GetAddress(topic Topic, asymmetric bool, key string) (PssAddress, error)

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) GetPeerTopics

func (pssapi *API) GetPeerTopics(pubkeyhex string) ([]Topic, error)

func (*API) GetPublicKey

func (pssapi *API) GetPublicKey() (keybytes hexutil.Bytes)

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 (pssapi *API) GetSymmetricKey(symkeyid string) (hexutil.Bytes, error)

func (*API) Receive

func (pssapi *API) Receive(ctx context.Context, topic Topic) (*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) SendAsym

func (pssapi *API) SendAsym(pubkeyhex string, topic Topic, msg hexutil.Bytes) error

func (*API) SendSym

func (pssapi *API) SendSym(symkeyhex string, topic Topic, msg hexutil.Bytes) error

func (*API) SetPeerPublicKey

func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic Topic, addr PssAddress) error

Set Public key to associate with a particular Pss peer

func (*API) StringToTopic

func (pssapi *API) StringToTopic(topicstring string) (Topic, error)

type APIMsg

type APIMsg struct {
	Msg        hexutil.Bytes
	Asymmetric bool
	Key        string
}

Wrapper for receiving pss messages when using the pss API providing access to sender of message

type Handler

type Handler func(msg []byte, p *p2p.Peer, asymmetric bool, keyid string) error

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

func (*HandshakeAPI) SendSym

func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes) (err error)

Send symmetric message under the handshake scheme

Overloads the pss.SendSym() API call, adding symmetric key usage count for message expiry control

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 Ping

type Ping struct {
	Pong bool      // toggle pong reply upon ping receive
	OutC chan bool // trigger ping
	InC  chan bool // optional, report back to calling code
}

type PingMsg

type PingMsg struct {
	Created time.Time
	Pong    bool // set if message is pong reply
}

Generic ping protocol implementation for pss devp2p protocol emulation

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

func (p *Protocol) Handle(msg []byte, peer *p2p.Peer, asymmetric bool, keyid string) error

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

func (p *Protocol) RemovePeer(asymmetric bool, key string)

type ProtocolMsg

type ProtocolMsg struct {
	Code       uint64
	Size       uint32
	Payload    []byte
	ReceivedAt time.Time
}

Convenience wrapper for devp2p protocol messages for transport over pss

type ProtocolParams

type ProtocolParams struct {
	Asymmetric bool
	Symmetric  bool
}

Protocol options to be passed to a new Protocol instance

The parameters specify which encryption schemes to allow

type Pss

type Pss struct {
	network.Overlay // we can get the overlayaddress 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

func NewPss(k network.Overlay, params *PssParams) (*Pss, error)

Creates a new Pss instance.

In addition to params, it takes a swarm network overlay and a FileStore storage for message cache storage.

func (*Pss) APIs

func (p *Pss) APIs() []rpc.API

func (*Pss) BaseAddr

func (p *Pss) BaseAddr() []byte

Returns the swarm overlay address of the pss node

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

func (p *Pss) GetSymmetricKey(symkeyid string) ([]byte, error)

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) Protocols

func (p *Pss) Protocols() []p2p.Protocol

func (*Pss) PublicKey

func (p *Pss) PublicKey() *ecdsa.PublicKey

Returns the pss node's public key

func (*Pss) Register

func (p *Pss) Register(topic *Topic, handler Handler) func()

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) Run

func (p *Pss) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error

func (*Pss) SendAsym

func (p *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error

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

func (p *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error

Send a message using symmetric encryption

Fails if the key id does not match any of the stored symmetric keys

func (*Pss) SetPeerPublicKey

func (p *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic 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 (*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())

func (*Pss) Start

func (p *Pss) Start(srv *p2p.Server) error

func (*Pss) Stop

func (p *Pss) Stop() error

func (*Pss) String

func (p *Pss) String() string

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 PssMsg

type PssMsg struct {
	To      []byte
	Control []byte
	Expire  uint32
	Payload *whisper.Envelope
}

PssMsg encapsulates messages transported over pss.

func (*PssMsg) String

func (msg *PssMsg) String() string

String representation of PssMsg

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 NewPssParams

func NewPssParams() *PssParams

Sane defaults for Pss

func (*PssParams) WithPrivateKey

func (params *PssParams) WithPrivateKey(privatekey *ecdsa.PrivateKey) *PssParams

type PssReadWriter

type PssReadWriter struct {
	*Pss
	LastActive time.Time
	// contains filtered or unexported fields
}

PssReadWriter bridges pss send/receive with devp2p protocol send/receive

Implements p2p.MsgReadWriter

func (*PssReadWriter) ReadMsg

func (prw *PssReadWriter) ReadMsg() (p2p.Msg, error)

Implements p2p.MsgReader

func (*PssReadWriter) WriteMsg

func (prw *PssReadWriter) WriteMsg(msg p2p.Msg) error

Implements p2p.MsgWriter

type Topic

type Topic whisper.TopicType

Topic is the PSS encapsulation of the Whisper topic type

func BytesToTopic

func BytesToTopic(b []byte) Topic

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

func ProtocolTopic(spec *protocols.Spec) Topic

Uniform translation of protocol specifiers to topic

func (Topic) MarshalJSON

func (t Topic) MarshalJSON() (b []byte, err error)

MarshalJSON implements the json.Marshaler interface

func (*Topic) String

func (t *Topic) String() string

func (*Topic) UnmarshalJSON

func (t *Topic) UnmarshalJSON(input []byte) error

MarshalJSON implements the json.Marshaler interface

Directories

Path Synopsis
simple abstraction for implementing pss functionality
simple abstraction for implementing pss functionality

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL