github.com/amazechain/amc@v0.1.3/internal/p2p/rpc_topic_mappings.go (about)

     1  package p2p
     2  
     3  import (
     4  	"github.com/amazechain/amc/api/protocol/sync_pb"
     5  	ssztype "github.com/amazechain/amc/common/types/ssz"
     6  	"reflect"
     7  
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  // SchemaVersionV1 specifies the schema version for our rpc protocol ID.
    12  const SchemaVersionV1 = "/1"
    13  
    14  // Specifies the protocol prefix for all our Req/Resp topics.
    15  const protocolPrefix = "/rpc"
    16  
    17  // StatusMessageName specifies the name for the status message topic.
    18  const StatusMessageName = "/status"
    19  
    20  // GoodbyeMessageName specifies the name for the goodbye message topic.
    21  const GoodbyeMessageName = "/goodbye"
    22  
    23  // PingMessageName Specifies the name for the ping message topic.
    24  const PingMessageName = "/ping"
    25  
    26  // BodiesByRangeMessageName specifies the name for the Bodies by range message topic.
    27  const BodiesByRangeMessageName = "/bodies_by_range"
    28  
    29  // HeadersByRangeMessageName specifies the name for the Headers by range message topic.
    30  const HeadersByRangeMessageName = "/headers_by_range"
    31  
    32  const (
    33  	// V1 RPC Topics
    34  	// RPCStatusTopicV1 defines the v1 topic for the status rpc method.
    35  	RPCStatusTopicV1 = protocolPrefix + StatusMessageName + SchemaVersionV1
    36  	// RPCGoodByeTopicV1 defines the v1 topic for the goodbye rpc method.
    37  	RPCGoodByeTopicV1 = protocolPrefix + GoodbyeMessageName + SchemaVersionV1
    38  	// RPCPingTopicV1 defines the v1 topic for the ping rpc method.
    39  	RPCPingTopicV1 = protocolPrefix + PingMessageName + SchemaVersionV1
    40  
    41  	// RPCBodiesDataTopicV1 defines the v1 topic for the Bodies rpc method.
    42  	RPCBodiesDataTopicV1 = protocolPrefix + BodiesByRangeMessageName + SchemaVersionV1
    43  
    44  	// RPCHeadersDataTopicV1 defines the v1 topic for the Headers rpc method.
    45  	RPCHeadersDataTopicV1 = protocolPrefix + HeadersByRangeMessageName + SchemaVersionV1
    46  )
    47  
    48  // RPC errors for topic parsing.
    49  const (
    50  	invalidRPCMessageType = "provided message type doesn't have a registered mapping"
    51  )
    52  
    53  // RPCTopicMappings map the base message type to the rpc request.
    54  var RPCTopicMappings = map[string]interface{}{
    55  	// RPC Status Message
    56  	RPCStatusTopicV1:     new(sync_pb.Status),
    57  	RPCBodiesDataTopicV1: new(sync_pb.BodiesByRangeRequest),
    58  
    59  	RPCPingTopicV1:    new(ssztype.SSZUint64),
    60  	RPCGoodByeTopicV1: new(ssztype.SSZUint64),
    61  }
    62  
    63  // Maps all registered protocol prefixes.
    64  var protocolMapping = map[string]bool{
    65  	protocolPrefix: true,
    66  }
    67  
    68  // Maps all the protocol message names for the different rpc
    69  // topics.
    70  var messageMapping = map[string]bool{
    71  	StatusMessageName:         true,
    72  	GoodbyeMessageName:        true,
    73  	PingMessageName:           true,
    74  	BodiesByRangeMessageName:  true,
    75  	HeadersByRangeMessageName: true,
    76  }
    77  
    78  var versionMapping = map[string]bool{
    79  	SchemaVersionV1: true,
    80  }
    81  
    82  // VerifyTopicMapping verifies that the topic and its accompanying
    83  // message type is correct.
    84  func VerifyTopicMapping(topic string, msg interface{}) error {
    85  	msgType, ok := RPCTopicMappings[topic]
    86  	if !ok {
    87  		return errors.New("rpc topic is not registered currently")
    88  	}
    89  	receivedType := reflect.TypeOf(msg)
    90  	registeredType := reflect.TypeOf(msgType)
    91  	typeMatches := registeredType.AssignableTo(receivedType)
    92  
    93  	if !typeMatches {
    94  		return errors.Errorf("accompanying message type is incorrect for topic: wanted %v  but got %v",
    95  			registeredType.String(), receivedType.String())
    96  	}
    97  	return nil
    98  }
    99  
   100  // TopicDeconstructor splits the provided topic to its logical sub-sections.
   101  // It is assumed all input topics will follow the specific schema:
   102  // /protocol-prefix/message-name/schema-version/...
   103  // For the purposes of deconstruction, only the first 3 components are
   104  // relevant.
   105  func TopicDeconstructor(topic string) (string, string, string, error) {
   106  	origTopic := topic
   107  	protPrefix := ""
   108  	message := ""
   109  	version := ""
   110  
   111  	// Iterate through all the relevant mappings to find the relevant prefixes,messages
   112  	// and version for this topic.
   113  	for k := range protocolMapping {
   114  		keyLen := len(k)
   115  		if keyLen > len(topic) {
   116  			continue
   117  		}
   118  		if topic[:keyLen] == k {
   119  			protPrefix = k
   120  			topic = topic[keyLen:]
   121  		}
   122  	}
   123  
   124  	if protPrefix == "" {
   125  		return "", "", "", errors.Errorf("unable to find a valid protocol prefix for %s", origTopic)
   126  	}
   127  
   128  	for k := range messageMapping {
   129  		keyLen := len(k)
   130  		if keyLen > len(topic) {
   131  			continue
   132  		}
   133  		if topic[:keyLen] == k {
   134  			message = k
   135  			topic = topic[keyLen:]
   136  		}
   137  	}
   138  
   139  	if message == "" {
   140  		return "", "", "", errors.Errorf("unable to find a valid message for %s", origTopic)
   141  	}
   142  
   143  	for k := range versionMapping {
   144  		keyLen := len(k)
   145  		if keyLen > len(topic) {
   146  			continue
   147  		}
   148  		if topic[:keyLen] == k {
   149  			version = k
   150  			topic = topic[keyLen:]
   151  		}
   152  	}
   153  
   154  	if version == "" {
   155  		return "", "", "", errors.Errorf("unable to find a valid schema version for %s", origTopic)
   156  	}
   157  
   158  	return protPrefix, message, version, nil
   159  }
   160  
   161  // RPCTopic is a type used to denote and represent a req/resp topic.
   162  type RPCTopic string
   163  
   164  // ProtocolPrefix returns the protocol prefix of the rpc topic.
   165  func (r RPCTopic) ProtocolPrefix() string {
   166  	prefix, _, _, err := TopicDeconstructor(string(r))
   167  	if err != nil {
   168  		return ""
   169  	}
   170  	return prefix
   171  }
   172  
   173  // MessageType returns the message type of the rpc topic.
   174  func (r RPCTopic) MessageType() string {
   175  	_, message, _, err := TopicDeconstructor(string(r))
   176  	if err != nil {
   177  		return ""
   178  	}
   179  	return message
   180  }
   181  
   182  // Version returns the schema version of the rpc topic.
   183  func (r RPCTopic) Version() string {
   184  	_, _, version, err := TopicDeconstructor(string(r))
   185  	if err != nil {
   186  		return ""
   187  	}
   188  	return version
   189  }
   190  
   191  // TopicFromMessage constructs the rpc topic from the provided message
   192  // type and epoch.
   193  func TopicFromMessage(msg string) (string, error) {
   194  	if !messageMapping[msg] {
   195  		return "", errors.Errorf("%s: %s", invalidRPCMessageType, msg)
   196  	}
   197  	version := SchemaVersionV1
   198  
   199  	return protocolPrefix + msg + version, nil
   200  }