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 }