git.gammaspectra.live/P2Pool/consensus/v3@v3.8.0/monero/client/levin/levin.go (about)

     1  //
     2  // see https://github.com/monero-project/monero/blob/e45619e61e4831eea70a43fe6985f4d57ea02e9e/contrib/epee/include/net/levin_base.h
     3  // see https://github.com/monero-project/monero/blob/e45619e61e4831eea70a43fe6985f4d57ea02e9e/docs/LEVIN_PROTOCOL.md
     4  
     5  package levin
     6  
     7  import (
     8  	"encoding/binary"
     9  	"fmt"
    10  )
    11  
    12  const (
    13  	LevinSignature uint64 = 0x0101010101012101 // Dander's Nightmare
    14  
    15  	LevinProtocolVersion uint32 = 1
    16  
    17  	LevinPacketRequest        uint32 = 0x00000001 // Q flag
    18  	LevinPacketReponse        uint32 = 0x00000002 // S flag
    19  	LevinPacketMaxDefaultSize uint64 = 100000000  // 100MB _after_ handshake
    20  	LevinPacketMaxInitialSize uint64 = 256 * 1024 // 256KiB _before_ handshake
    21  
    22  	LevinHeaderSizeBytes = 33
    23  )
    24  
    25  const (
    26  	// Return Codes.
    27  	LevinOk                               int32 = 0
    28  	LevinErrorConnection                  int32 = -1
    29  	LevinErrorConnectionNotFound          int32 = -2
    30  	LevinErrorConnectionDestroyed         int32 = -3
    31  	LevinErrorConnectionTimedout          int32 = -4
    32  	LevinErrorConnectionNoDuplexProtocol  int32 = -5
    33  	LevinErrorConnectionHandlerNotDefined int32 = -6
    34  	LevinErrorFormat                      int32 = -7
    35  )
    36  
    37  func IsValidReturnCode(c int32) bool {
    38  	// anything >= 0 is good (there are some `1`s in the code :shrug:)
    39  	return c >= LevinErrorFormat
    40  }
    41  
    42  const (
    43  	// p2p admin commands.
    44  	CommandHandshake    uint32 = 1001
    45  	CommandTimedSync    uint32 = 1002
    46  	CommandPing         uint32 = 1003
    47  	CommandStat         uint32 = 1004
    48  	CommandNetworkState uint32 = 1005
    49  	CommandPeerID       uint32 = 1006
    50  	CommandSupportFlags uint32 = 1007
    51  )
    52  
    53  var (
    54  	MainnetNetworkId = []byte{
    55  		0x12, 0x30, 0xf1, 0x71,
    56  		0x61, 0x04, 0x41, 0x61,
    57  		0x17, 0x31, 0x00, 0x82,
    58  		0x16, 0xa1, 0xa1, 0x10,
    59  	}
    60  
    61  	MainnetGenesisTx = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"
    62  )
    63  
    64  func IsValidCommand(c uint32) bool {
    65  	return (c >= CommandHandshake && c <= CommandSupportFlags)
    66  }
    67  
    68  //
    69  // Header
    70  //
    71  //
    72  //       0               1               2               3
    73  //       0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
    74  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    75  //      |      0x01     |      0x21     |      0x01     |      0x01     |
    76  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    77  //      |      0x01     |      0x01     |      0x01     |      0x01     |
    78  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    79  //      |                             Length                            |
    80  //      |                                                               |
    81  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    82  //      |  E. Response  |               _   Command     _
    83  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    84  //      		|               _ Return Code   _
    85  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    86  //      		|Q|S|B|E|       _       Reserved_
    87  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    88  //      		|      0x01     |      0x00     |      0x00     |
    89  //      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    90  //      |     0x00      |
    91  //      +-+-+-+-+-+-+-+-+
    92  //
    93  //
    94  // i.e.,
    95  //
    96  //	BYTE(0X01) BYTE(0X21) BYTE(0X01) BYTE(0X01)  ---.
    97  //							+--> protocol identification
    98  //	BYTE(0X01) BYTE(0X01) BYTE(0X01) BYTE(0X01)  ---'
    99  //
   100  //
   101  //	UINT64(LENGTH)	-----------------------------------> unsigned little-endian 64bit integer
   102  //							     length of the payload _not including_
   103  //							     the header. messages >100MB are rejected.
   104  //
   105  //
   106  //	BYTE(E.RESPONSE) 4BYTE(COMMAND) 4BYTE(RET CODE)
   107  //         |               |		  |
   108  //         |               |		  |
   109  //         |               |	          '->  signed 32-bit little endian integer representing the response
   110  //         |               |		       from the peer from the last command invoked. `0` for request msgs.
   111  //         |               |
   112  //         |               '-> unsigned 32-bit little endian integer
   113  //         |                   representing the monero specific cmd
   114  //         |
   115  //         '-> zero-byte if no response is expected from the peer, non-zero if response is expected.
   116  //	       peers must respond to requests w/ this flag in the same order as received.
   117  //
   118  //
   119  //	BIT(Q) BIT(S) BIT(B) BIT(E) 3BYTE+4BIT(RESERVED)
   120  //         |    |      |      |
   121  //         |    |      |      |
   122  //         |    |      |      '-> set if this is the end of a frag msg
   123  //         |    |      |
   124  //         |    |      '-> set if this is the beginning of a frag msg
   125  //         |    |
   126  //         |    '-> set if the message is a response
   127  //         |
   128  //         '-> set if the message is a request
   129  //
   130  //
   131  //
   132  //	BYTE(0X01) BYTE(0X00) BYTE(0X00) BYTE(0X00)
   133  //         |
   134  //         '--> version
   135  //
   136  type Header struct {
   137  	Signature       uint64
   138  	Length          uint64
   139  	ExpectsResponse bool
   140  	Command         uint32
   141  	ReturnCode      int32
   142  	Flags           uint32 // only 4 most significant bits matter (Q|S|B|E)
   143  	Version         uint32
   144  }
   145  
   146  func NewRequestHeader(command uint32, length uint64) *Header {
   147  	return &Header{
   148  		Signature:       LevinSignature,
   149  		Length:          length,
   150  		ExpectsResponse: true,
   151  		Command:         command,
   152  		ReturnCode:      0,
   153  		Flags:           LevinPacketRequest,
   154  		Version:         LevinProtocolVersion,
   155  	}
   156  }
   157  
   158  func NewHeaderFromBytesBytes(bytes []byte) (*Header, error) {
   159  	if len(bytes) != LevinHeaderSizeBytes {
   160  		return nil, fmt.Errorf("invalid header size: expected %d, has %d",
   161  			LevinHeaderSizeBytes, len(bytes),
   162  		)
   163  	}
   164  
   165  	var (
   166  		size = 0
   167  		idx  = 0
   168  	)
   169  
   170  	header := &Header{}
   171  
   172  	{ // signature
   173  		size = 8
   174  		header.Signature = binary.LittleEndian.Uint64(bytes[idx : idx+size])
   175  		idx += size
   176  
   177  		if header.Signature != LevinSignature {
   178  			return nil, fmt.Errorf("signature mismatch: expected %x, got %x",
   179  				LevinSignature, header.Signature,
   180  			)
   181  		}
   182  	}
   183  
   184  	{ // length
   185  		size = 8
   186  		header.Length = binary.LittleEndian.Uint64(bytes[idx : idx+size])
   187  		idx += size
   188  	}
   189  
   190  	{ // expects response
   191  		size = 1
   192  		header.ExpectsResponse = (bytes[idx] != 0)
   193  		idx += size
   194  	}
   195  
   196  	{ // command
   197  		size = 4
   198  		header.Command = binary.LittleEndian.Uint32(bytes[idx : idx+size])
   199  		idx += size
   200  
   201  		if !IsValidCommand(header.Command) {
   202  			return nil, fmt.Errorf("invalid command %d", header.Command)
   203  		}
   204  	}
   205  
   206  	{ // return code
   207  		size = 4
   208  		header.ReturnCode = int32(binary.LittleEndian.Uint32(bytes[idx : idx+size]))
   209  		idx += size
   210  
   211  		if !IsValidReturnCode(header.ReturnCode) {
   212  			return nil, fmt.Errorf("invalid return code %d", header.ReturnCode)
   213  		}
   214  	}
   215  
   216  	{ // flags
   217  		size = 4
   218  		header.Flags = binary.LittleEndian.Uint32(bytes[idx : idx+size])
   219  		idx += size
   220  	}
   221  
   222  	{ // version
   223  		size = 4
   224  		header.Version = binary.LittleEndian.Uint32(bytes[idx : idx+size])
   225  		idx += size
   226  
   227  		if header.Version != LevinProtocolVersion {
   228  			return nil, fmt.Errorf("invalid version %x",
   229  				header.Version)
   230  		}
   231  	}
   232  
   233  	return header, nil
   234  }
   235  
   236  func (h *Header) Bytes() []byte {
   237  	var (
   238  		header = make([]byte, LevinHeaderSizeBytes) // full header
   239  		b      = make([]byte, 8)                    // biggest type
   240  
   241  		idx  = 0
   242  		size = 0
   243  	)
   244  
   245  	{ // signature
   246  		size = 8
   247  
   248  		binary.LittleEndian.PutUint64(b, h.Signature)
   249  		copy(header[idx:], b[:size])
   250  		idx += size
   251  	}
   252  
   253  	{ // length
   254  		size = 8
   255  
   256  		binary.LittleEndian.PutUint64(b, h.Length)
   257  		copy(header[idx:], b[:size])
   258  		idx += size
   259  	}
   260  
   261  	{ // expects response
   262  		size = 1
   263  
   264  		if h.ExpectsResponse {
   265  			b[0] = 0x01
   266  		} else {
   267  			b[0] = 0x00
   268  		}
   269  
   270  		copy(header[idx:], b[:size])
   271  		idx += size
   272  	}
   273  
   274  	{ // command
   275  		size = 4
   276  
   277  		binary.LittleEndian.PutUint32(b, h.Command)
   278  		copy(header[idx:], b[:size])
   279  		idx += size
   280  	}
   281  
   282  	{ // return code
   283  		size = 4
   284  
   285  		binary.LittleEndian.PutUint32(b, uint32(h.ReturnCode))
   286  		copy(header[idx:], b[:size])
   287  		idx += size
   288  	}
   289  
   290  	{ // flags
   291  		size = 4
   292  
   293  		binary.LittleEndian.PutUint32(b, h.Flags)
   294  		copy(header[idx:], b[:size])
   295  		idx += size
   296  	}
   297  
   298  	{ // version
   299  		size = 4
   300  
   301  		binary.LittleEndian.PutUint32(b, h.Version)
   302  		copy(header[idx:], b[:size])
   303  		idx += size
   304  	}
   305  
   306  	return header
   307  }