This repository has been archived on 2024-04-07. You can view files and clone it, but cannot push or open issues or pull requests.
go-monero/pkg/levin/levin.go
Ciro S. Costa ce6332cc39 pkg: source formatting
Signed-off-by: Ciro S. Costa <utxobr@protonmail.com>
2021-06-12 11:42:51 -04:00

308 lines
7.8 KiB
Go

//
// see https://github.com/monero-project/monero/blob/e45619e61e4831eea70a43fe6985f4d57ea02e9e/contrib/epee/include/net/levin_base.h
// see https://github.com/monero-project/monero/blob/e45619e61e4831eea70a43fe6985f4d57ea02e9e/docs/LEVIN_PROTOCOL.md
package levin
import (
"encoding/binary"
"fmt"
)
const (
LevinSignature uint64 = 0x0101010101012101 // Dander's Nightmare
LevinProtocolVersion uint32 = 1
LevinPacketRequest uint32 = 0x00000001 // Q flag
LevinPacketReponse uint32 = 0x00000002 // S flag
LevinPacketMaxDefaultSize uint64 = 100000000 // 100MB _after_ handshake
LevinPacketMaxInitialSize uint64 = 256 * 1024 // 256KiB _before_ handshake
LevinHeaderSizeBytes = 33
)
const (
// Return Codes.
LevinOk int32 = 0
LevinErrorConnection int32 = -1
LevinErrorConnectionNotFound int32 = -2
LevinErrorConnectionDestroyed int32 = -3
LevinErrorConnectionTimedout int32 = -4
LevinErrorConnectionNoDuplexProtocol int32 = -5
LevinErrorConnectionHandlerNotDefined int32 = -6
LevinErrorFormat int32 = -7
)
func IsValidReturnCode(c int32) bool {
// anything >= 0 is good (there are some `1`s in the code :shrug:)
return c >= LevinErrorFormat
}
const (
// p2p admin commands.
CommandHandshake uint32 = 1001
CommandTimedSync uint32 = 1002
CommandPing uint32 = 1003
CommandStat uint32 = 1004
CommandNetworkState uint32 = 1005
CommandPeerID uint32 = 1006
CommandSupportFlags uint32 = 1007
)
var (
MainnetNetworkId = []byte{
0x12, 0x30, 0xf1, 0x71,
0x61, 0x04, 0x41, 0x61,
0x17, 0x31, 0x00, 0x82,
0x16, 0xa1, 0xa1, 0x10,
}
MainnetGenesisTx = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"
)
func IsValidCommand(c uint32) bool {
return (c >= CommandHandshake && c <= CommandSupportFlags)
}
//
// Header
//
//
// 0 1 2 3
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x01 | 0x21 | 0x01 | 0x01 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x01 | 0x01 | 0x01 | 0x01 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Length |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | E. Response | _ Command _
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | _ Return Code _
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Q|S|B|E| _ Reserved_
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x01 | 0x00 | 0x00 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0x00 |
// +-+-+-+-+-+-+-+-+
//
//
// i.e.,
//
// BYTE(0X01) BYTE(0X21) BYTE(0X01) BYTE(0X01) ---.
// +--> protocol identification
// BYTE(0X01) BYTE(0X01) BYTE(0X01) BYTE(0X01) ---'
//
//
// UINT64(LENGTH) -----------------------------------> unsigned little-endian 64bit integer
// length of the payload _not including_
// the header. messages >100MB are rejected.
//
//
// BYTE(E.RESPONSE) 4BYTE(COMMAND) 4BYTE(RET CODE)
// | | |
// | | |
// | | '-> signed 32-bit little endian integer representing the response
// | | from the peer from the last command invoked. `0` for request msgs.
// | |
// | '-> unsigned 32-bit little endian integer
// | representing the monero specific cmd
// |
// '-> zero-byte if no response is expected from the peer, non-zero if response is expected.
// peers must respond to requests w/ this flag in the same order as received.
//
//
// BIT(Q) BIT(S) BIT(B) BIT(E) 3BYTE+4BIT(RESERVED)
// | | | |
// | | | |
// | | | '-> set if this is the end of a frag msg
// | | |
// | | '-> set if this is the beginning of a frag msg
// | |
// | '-> set if the message is a response
// |
// '-> set if the message is a request
//
//
//
// BYTE(0X01) BYTE(0X00) BYTE(0X00) BYTE(0X00)
// |
// '--> version
//
type Header struct {
Signature uint64
Length uint64
ExpectsResponse bool
Command uint32
ReturnCode int32
Flags uint32 // only 4 most significant bits matter (Q|S|B|E)
Version uint32
}
func NewRequestHeader(command uint32, length uint64) *Header {
return &Header{
Signature: LevinSignature,
Length: length,
ExpectsResponse: true,
Command: command,
ReturnCode: 0,
Flags: LevinPacketRequest,
Version: LevinProtocolVersion,
}
}
func NewHeaderFromBytesBytes(bytes []byte) (*Header, error) {
if len(bytes) != LevinHeaderSizeBytes {
return nil, fmt.Errorf("invalid header size: expected %d, has %d",
LevinHeaderSizeBytes, len(bytes),
)
}
var (
size = 0
idx = 0
)
header := &Header{}
{ // signature
size = 8
header.Signature = binary.LittleEndian.Uint64(bytes[idx : idx+size])
idx += size
if header.Signature != LevinSignature {
return nil, fmt.Errorf("signature mismatch: expected %x, got %x",
LevinSignature, header.Signature,
)
}
}
{ // length
size = 8
header.Length = binary.LittleEndian.Uint64(bytes[idx : idx+size])
idx += size
}
{ // expects response
size = 1
header.ExpectsResponse = (bytes[idx] != 0)
idx += size
}
{ // command
size = 4
header.Command = binary.LittleEndian.Uint32(bytes[idx : idx+size])
idx += size
if !IsValidCommand(header.Command) {
return nil, fmt.Errorf("invalid command %d", header.Command)
}
}
{ // return code
size = 4
header.ReturnCode = int32(binary.LittleEndian.Uint32(bytes[idx : idx+size]))
idx += size
if !IsValidReturnCode(header.ReturnCode) {
return nil, fmt.Errorf("invalid return code %d", header.ReturnCode)
}
}
{ // flags
size = 4
header.Flags = binary.LittleEndian.Uint32(bytes[idx : idx+size])
idx += size
}
{ // version
size = 4
header.Version = binary.LittleEndian.Uint32(bytes[idx : idx+size])
idx += size
if header.Version != LevinProtocolVersion {
return nil, fmt.Errorf("invalid version %x",
header.Version)
}
}
return header, nil
}
func (h *Header) Bytes() []byte {
var (
header = make([]byte, LevinHeaderSizeBytes) // full header
b = make([]byte, 8) // biggest type
idx = 0
size = 0
)
{ // signature
size = 8
binary.LittleEndian.PutUint64(b, h.Signature)
copy(header[idx:], b[:size])
idx += size
}
{ // length
size = 8
binary.LittleEndian.PutUint64(b, h.Length)
copy(header[idx:], b[:size])
idx += size
}
{ // expects response
size = 1
if h.ExpectsResponse {
b[0] = 0x01
} else {
b[0] = 0x00
}
copy(header[idx:], b[:size])
idx += size
}
{ // command
size = 4
binary.LittleEndian.PutUint32(b, h.Command)
copy(header[idx:], b[:size])
idx += size
}
{ // return code
size = 4
binary.LittleEndian.PutUint32(b, uint32(h.ReturnCode))
copy(header[idx:], b[:size])
idx += size
}
{ // flags
size = 4
binary.LittleEndian.PutUint32(b, h.Flags)
copy(header[idx:], b[:size])
idx += size
}
{ // version
size = 4
binary.LittleEndian.PutUint32(b, h.Version)
copy(header[idx:], b[:size])
idx += size
}
return header
}