tutus-chain/pkg/network/capability/capability.go

192 lines
4.6 KiB
Go
Executable File

package capability
import (
"errors"
"git.marketally.com/tutus-one/tutus-chain/pkg/io"
)
const (
// MaxCapabilities is the maximum number of capabilities per payload.
MaxCapabilities = 32
// MaxDataSize is the maximum size of capability payload.
MaxDataSize = 1024
)
// Capabilities is a list of Capability.
type Capabilities []Capability
// IsArchivalNode denotes whether the node has Archival capability.
func (cs *Capabilities) IsArchivalNode() bool {
for _, c := range *cs {
if c.Type == ArchivalNode {
return true
}
}
return false
}
// DecodeBinary implements io.Serializable.
func (cs *Capabilities) DecodeBinary(br *io.BinReader) {
br.ReadArray(cs, MaxCapabilities)
if br.Err == nil {
br.Err = cs.checkUniqueCapabilities()
}
}
// EncodeBinary implements io.Serializable.
func (cs *Capabilities) EncodeBinary(br *io.BinWriter) {
br.WriteArray(*cs)
}
// checkUniqueCapabilities checks whether payload capabilities have a unique type.
func (cs Capabilities) checkUniqueCapabilities() error {
err := errors.New("capabilities with the same type are not allowed")
var isFullNode, isArchived, isTCP, isWS, isDisabledCompression bool
for _, cap := range cs {
switch cap.Type {
case ArchivalNode:
if isArchived {
return err
}
isArchived = true
case FullNode:
if isFullNode {
return err
}
isFullNode = true
case DisableCompressionNode:
if isDisabledCompression {
return err
}
isDisabledCompression = true
case TCPServer:
if isTCP {
return err
}
isTCP = true
case WSServer:
if isWS {
return err
}
isWS = true
default: /* OK to have duplicates */
}
}
return nil
}
// Capability describes a network service available for the node.
type Capability struct {
Type Type
Data io.Serializable
}
// DecodeBinary implements io.Serializable.
func (c *Capability) DecodeBinary(br *io.BinReader) {
c.Type = Type(br.ReadB())
switch c.Type {
case ArchivalNode:
c.Data = &Archival{}
case FullNode:
c.Data = &Node{}
case DisableCompressionNode:
c.Data = &DisableCompression{}
case TCPServer, WSServer:
c.Data = &Server{}
default:
c.Data = &Unknown{}
}
c.Data.DecodeBinary(br)
}
// EncodeBinary implements io.Serializable.
func (c *Capability) EncodeBinary(bw *io.BinWriter) {
if c.Data == nil {
bw.Err = errors.New("capability has no data")
return
}
bw.WriteB(byte(c.Type))
c.Data.EncodeBinary(bw)
}
// Node represents full node capability with a start height.
type Node struct {
StartHeight uint32
}
// DecodeBinary implements io.Serializable.
func (n *Node) DecodeBinary(br *io.BinReader) {
n.StartHeight = br.ReadU32LE()
}
// EncodeBinary implements io.Serializable.
func (n *Node) EncodeBinary(bw *io.BinWriter) {
bw.WriteU32LE(n.StartHeight)
}
// Server represents TCP or WS server capability with a port.
type Server struct {
// Port is the port this server is listening on.
Port uint16
}
// DecodeBinary implements io.Serializable.
func (s *Server) DecodeBinary(br *io.BinReader) {
s.Port = br.ReadU16LE()
}
// EncodeBinary implements io.Serializable.
func (s *Server) EncodeBinary(bw *io.BinWriter) {
bw.WriteU16LE(s.Port)
}
// Archival represents an archival node that stores all blocks.
type Archival struct{}
// DecodeBinary implements io.Serializable.
func (a *Archival) DecodeBinary(br *io.BinReader) {
var zero = br.ReadB() // Zero-length byte array as per Unknown.
if zero != 0 {
br.Err = errors.New("archival capability with non-zero data")
}
}
// EncodeBinary implements io.Serializable.
func (a *Archival) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(0)
}
// DisableCompression represents the node that doesn't compress any P2P payloads.
type DisableCompression struct{}
// DecodeBinary implements io.Serializable.
func (d *DisableCompression) DecodeBinary(br *io.BinReader) {
var zero = br.ReadB() // Zero-length byte array as per Unknown.
if zero != 0 {
br.Err = errors.New("DisableCompression capability with non-zero data")
}
}
// EncodeBinary implements io.Serializable.
func (d *DisableCompression) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(0)
}
// Unknown represents an unknown capability with some data. Other nodes can
// decode it even if they can't interpret it. This is not expected to be used
// for sending data directly (proper new types should be used), but it allows
// for easier protocol extensibility (old nodes won't reject new capabilities).
type Unknown []byte
// DecodeBinary implements io.Serializable.
func (u *Unknown) DecodeBinary(br *io.BinReader) {
*u = br.ReadVarBytes()
}
// EncodeBinary implements io.Serializable.
func (u *Unknown) EncodeBinary(bw *io.BinWriter) {
bw.WriteVarBytes(*u)
}