-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathpayloadExtensions.ts
175 lines (139 loc) · 4.78 KB
/
payloadExtensions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import { ByteListType, ContainerType, ListBasicType, UintBigintType, UintNumberType } from "@chainsafe/ssz";
import { bytesToHex, fromAscii, hexToBytes, toAscii } from "@ethereumjs/util";
/**
* Numeric identifier which tells clients how the payload field should be decoded.
*/
export const PingPongPayloadType = new UintNumberType(2)
/**
* SSZ encoded extension payload
*/
export const PingPongPayload = new ByteListType(1100)
/**
* All payloads used in the Ping custom_payload MUST follow the Ping Custom Payload Extensions format.
*/
export const CustomPayloadExtensionsFormat = new ContainerType({
type: PingPongPayloadType,
payload: PingPongPayload
})
/**
* A standard extension is an extension which all nodes on the network MUST support.
*/
enum StandardExtensions {
CLIENT_INFO_RADIUS_AND_CAPABILITIES = 0,
ERROR_RESPONSE = 65535
}
/**
* Non standard extensions are extensions in which you can't assume all other clients support.
* To use a non standard extension: Portal clients first send a Type 0 Payload packet, then upgrade to use their desired non standard extensions.
*/
enum NonStandardExtensions {
BASIC_RADIUS_PAYLOAD = 1,
HISTORY_RADIUS_PAYLOAD = 2,
}
export const PingPongPayloadExtensions = {
...StandardExtensions,
...NonStandardExtensions
}
export interface IClientInfo {
clientName: string;
clientVersionAndShortCommit: string;
operatingSystemAndCpuArchitecture: string;
programmingLanguageAndVersion: string;
}
export const MAX_CLIENT_INFO_BYTE_LENGTH = 200
/**
* Encode Client info as ASCII hex encoded string.
* @param clientInfo
* @returns
*/
export function encodeClientInfo(clientInfo: IClientInfo): Uint8Array {
const clientInfoBytes = hexToBytes(fromAscii(Object.values(clientInfo).join("/")))
if (clientInfoBytes.length > MAX_CLIENT_INFO_BYTE_LENGTH) {
throw new Error(`Client info is too long: ${clientInfoBytes.length} > ${MAX_CLIENT_INFO_BYTE_LENGTH}`)
}
return clientInfoBytes
}
export function decodeClientInfo(clientInfo: Uint8Array): IClientInfo {
const [clientName, clientVersionAndShortCommit, operatingSystemAndCpuArchitecture, programmingLanguageAndVersion] = toAscii(bytesToHex(clientInfo)).split("/");
return {
clientName,
clientVersionAndShortCommit,
operatingSystemAndCpuArchitecture,
programmingLanguageAndVersion
};
}
export const ClientInfo = new ByteListType(MAX_CLIENT_INFO_BYTE_LENGTH)
/**
* Type 0: Client Info and Capabilities
*
* clientInfo: ASCII hex encoded string
* capabilities: list of capabilities
*/
export const MAX_CAPABILITIES_LENGTH = 400
export const Capabilities = new ListBasicType(PingPongPayloadType, MAX_CAPABILITIES_LENGTH)
export const DataRadius = new UintBigintType(32)
export const ClientInfoAndCapabilities = new ContainerType({
ClientInfo,
DataRadius,
Capabilities
})
/**
* Type 0 Ping/Pong Payload
*/
export type CapabilitiesPayload = {
type: 0,
payload: ReturnType<typeof ClientInfoAndCapabilities.serialize>
}
/**
* Type 1: Basic Radius Payload
* A basic Ping/Pong payload which only contains the node's radius.
*/
export const BasicRadius = new ContainerType({
dataRadius: new UintBigintType(32)
})
/**
* Type 1 Ping/Pong Payload
*/
export type BasicRadiusPayload = {
type: 1,
payload: ReturnType<typeof BasicRadius.serialize>
}
/**
* Type 2: History Radius Payload
* A specialized radius payload for the history network which contains field for how many ephemeral headers the node holds.
*/
export const HistoryRadius = new ContainerType({
dataRadius: new UintBigintType(32),
ephemeralHeadersCount: new UintNumberType(2)
})
export type HistoryRadiusPayload = {
type: 2,
payload: ReturnType<typeof HistoryRadius.serialize>
}
/**
* type 65535: Error Response
* If the ping receiver can't handle the ping for any reason the pong should return an error payload
*/
export const MAX_ERROR_BYTE_LENGTH = 300
export const ErrorPayloadMessage = new ByteListType(MAX_ERROR_BYTE_LENGTH)
export const ErrorPayloadCode = new UintNumberType(2)
export const ErrorPayload = new ContainerType({
errorCode: ErrorPayloadCode,
message: ErrorPayloadMessage
})
export type ErrorResponsePayload = {
type: 65535,
payload: ReturnType<typeof ErrorPayload.serialize>
}
export enum PingPongErrorCodes {
EXTENSION_NOT_SUPPORTED = 0,
REQUESTED_DATA_NOT_FOUND = 1,
FAILED_TO_DECODE_PAYLOAD = 2,
SYSTEM_ERROR = 3,
}
export const PingPongCustomPayload = {
[PingPongPayloadExtensions.CLIENT_INFO_RADIUS_AND_CAPABILITIES]: ClientInfoAndCapabilities,
[PingPongPayloadExtensions.BASIC_RADIUS_PAYLOAD]: BasicRadius,
[PingPongPayloadExtensions.HISTORY_RADIUS_PAYLOAD]: HistoryRadius,
[PingPongPayloadExtensions.ERROR_RESPONSE]: ErrorPayload
}