Ethereum PHP
PHP interface to Ethereum JSON-RPC API.
Rlp.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Ethereum\RLP;
4 
8 
9 
18 class Rlp extends EthereumStatic
19 {
20 
21  // if a string is 0-55 bytes long, the RLP encoding consists
22  // of a single byte with value 0x80 plus the length of the string
23  // followed by the string. The range of the first byte is thus [0x80, 0xb7].
24  const THRESHOLD_LONG = 110; // As we count hex chars this value is 110
25  const PREF_SELF_CONTAINED = 127; // 0x7F
26  const OFFSET_SHORT_ITEM = 128; // 0x80
27  const OFFSET_LONG_ITEM = 183; // 0xb7;
28  const OFFSET_SHORT_LIST = 192; // 0xc0;
29  const OFFSET_LONG_LIST = 247; // 0xf7;
30 
31 
43  public static function encode(string $val)
44  {
45 
46  $val = self::removeHexPrefix($val);
47 
48  // Length in chars. Note: 2 chars equals one hex byte.
49  $length = strlen($val);
50 
51  // Offset where the value (the length byte actually) starts. This is only relevant
52  // in Arrays and lists which are not yet implemented.
53  $offset = '0x0000000000000000000000000000000000000000000000000000000000000020';
54 
55  if ($length % 2 && ctype_xdigit($val)) {
56  throw new InvalidArgumentException('Can not decode. Invalid hex value.');
57  }
58 
59  if ($length < self::THRESHOLD_LONG) {
60  // if a string is 0-55 bytes long, the RLP encoding consists
61  // of a single byte with value 0x80 plus the length of the
62  // string followed by the string. The range of the first byte is thus [0x80, 0xb7].
63  $lengthInByte = self::getByteLength($length / 2);
64 
65  }
66  else {
67  // If a string is more than 55 bytes long, the RLP encoding
68  // consists of a single byte with value 0xb7 plus the length
69  // in bytes of the length of the string in binary form, followed
70  // by the length of the string, followed by the string.
71  $lengthInByte = self::getByteLength($length / 2);
72  }
73 
74  return $offset . self::removeHexPrefix($lengthInByte) . self::padRight($val);
75  }
76 
85  private static function padRight($val)
86  {
87  $fillUp = 64 - (strlen($val) % 64);
88  if ($fillUp < 64) {
89  return $val . str_repeat("0", $fillUp);
90  }
91  return $val;
92  }
93 
100  public static function decode(string $hexVal): array
101  {
102  if (!is_string($hexVal) || !strlen($hexVal)) {
103  throw new InvalidArgumentException('RLP value length can not be "" or null.');
104  }
105  $rlpCollection = new RlpCollection();
106  $hexVal = self::removeHexPrefix($hexVal);
107 
108  // Data length in Chars ?.
109  $dataLength = strlen($hexVal);
110  self::decodeValues($hexVal, 0, 0, $dataLength, 1, $rlpCollection);
111  return $rlpCollection->get();
112  }
113 
123  private static function decodeValues(
124  string $msgData,
125  int $level,
126  int $startPos,
127  int $endPos,
128  int $levelToIndex,
129  RlpCollection $rlpCollection
130  ) {
131 
132  if (is_null($msgData) || strlen($msgData) === 0) {
133  // @todo if (msgData == null || msgData.Length == 0)
134  $X = false;
135  return;
136  }
137 
138  // var currentData = new byte[endPosition - startPosition];
139  // ???? $currentData = substr($msgData, $startPos, $endPos - $startPos);
140 
141  $currentPos = $startPos;
142  // ???
143  // Array.Copy(msgData, startPosition, currentData, 0, currentData.Length);
144 
145  try {
146  while ($currentPos < $endPos) {
147 
148  $prefix = self::getByteValueAtOffsetPos($msgData, $currentPos);
149 
150  // Is this sustainable????
151  // * Does not work decoding TWO single bytes
152  if ($prefix < $currentPos) {
153  break;
154  }
155 
156  $length = self::getLengthInByte($msgData, $currentPos);
157 
158  // prefix <= 0x7f
159  if ($prefix <= 127) {
160 
161  $currentPos = self::processSingleByteItem($msgData, $rlpCollection, $currentPos);
162 
163  } // elif prefix <= 0xb7 and length > prefix - 0x80:
164  elseif ($prefix <= 183 && $length > $prefix - 128) {
165  // strLen = prefix - 0x80
166 
167  // TODO WHY THERE IS NO DIFFERING PREFIX???
168 
169  $currentPos = self::processSingleByteItem($msgData, $rlpCollection, $currentPos);
170 
171  } // prefix <= 0xbf and length > prefix - 0xb7 and length > prefix - 0xb7 + to_integer(substr(input, 1, prefix - 0xb7)):
172  elseif ($prefix <= 191 && $length > $prefix - 183 && true) {
173  // lenOfStrLen = prefix - 0xb7
174  // strLen = to_integer(substr(input, 1, lenOfStrLen))
175  // return (1 + lenOfStrLen, strLen, str)
176  $XXX = false;
177 
178  throw new \Exception('Did not run into this yet @Rlp::decodeValues()');
179 
180  } // prefix <= 0xf7 and length > prefix - 0xc0:
181  elseif ($prefix <= 247 && $length > $prefix - 192) {
182  // listLen = prefix - 192;
183  // return (1, listLen, list)
184 
185 
186  // TODO WHY THERE IS NO DIFFERING PREFIX???
187 
188  $currentPos = self::processSingleByteItem($msgData, $rlpCollection, $currentPos);
189 
190  } // prefix <= 0xff and length > prefix - 0xf7 and length > prefix - 0xf7 + to_integer(substr(input, 1, prefix - 0xf7)):
191  elseif ($prefix <= 255 && $length > $prefix - 247 && true) {
192  // lenOfListLen = prefix - 0xf7
193  // listLen = to_integer(substr(input, 1, lenOfListLen))
194  // return (1 + lenOfListLen, listLen, list)
195  throw new \Exception('Did not run into this yet @Rlp::decodeValues()');
196  }
197  else {
198  throw new \Exception('Did not run into this yet @Rlp::decodeValues()');
199  }
200 
201 // if (self::isOutofBoundary($msgData, $currentPos)) {
202 // $XXX = FALSE;
203 // break;
204 // }
205 //
206 // if (self::isListBiggerThan55Bytes($msgData, $currentPos)) {
207 // // $currentPos = ProcessListBiggerThan55Bytes($msgData, level, levelToIndex, rlpCollection, $currentPos);
208 // $XXX = FALSE;
210 // }
211 //
212 // if (self::isListLessThan55Bytes($msgData, $currentPos)) {
213 // // $currentPos = ProcessListLessThan55Bytes($msgData, level, levelToIndex, rlpCollection, $currentPos);
214 // $XXX = FALSE;
216 // }
217 //
218 // if (self::isItemBiggerThan55Bytes($msgData, $currentPos)) {
219 // // $currentPos = ProcessItemBiggerThan55Bytes($msgData, rlpCollection, $currentPos);
220 // $XXX = FALSE;
222 // }
223 //
224 // if (self::isItemLessThan55Bytes($msgData, $currentPos)) {
225 // // $currentPos = ProcessItemLessThan55Bytes($msgData, rlpCollection, $currentPos);
226 // $XXX = FALSE;
228 // }
229 //
230 // if (self::isNullItem($msgData, $currentPos)) {
231 // // $currentPos = ProcessNullItem(rlpCollection, $currentPos);
232 // $XXX = FALSE;
234 // }
235 //
236 // if (self::isSigleByteItem($msgData, $currentPos)) {
237 // // currentPosition = ProcessleBleByteItem(msgData, rlpCollection, currentPosition);
238 // $XXX = FALSE;
239 // $currentPos = self::processSingleByteItem($msgData, $rlpCollection, $currentPos);
240 // }
241 
242  }
243  } catch (Exception $e) {
244  // "Invalid RLP " + currentData.ToHex(), ex);
245  throw new \Exception('Invalid RLP ');
246  }
247  }
248 
249 
256  // ProcessSingleByteItem(byte[] msgData, RLPCollection rlpCollection, int currentPosition)
257  protected static function processSingleByteItem(
258  string $msgData,
259  RLPCollection $rlpCollection,
260  int $currentPos
261  ) {
262  $thisData = substr($msgData, $currentPos);
263  $item = new RlpItem($thisData);
264  $rlpCollection->add($item);
265  return $item->getCharLength();
266  }
267 
268 
274  private static function byteLength(string $hex)
275  {
276  return hexdec($hex);
277  }
278 
279  private static function charLength(string $hex)
280  {
281  $hex = self::ensureHexPrefix($hex);
282  return 2 * hexdec($hex);
283  }
284 
285  protected static function getByteValueAt(string $msgData, int $pos)
286  {
287  return self::byteLength(substr($msgData, $pos, 64));
288  }
289 
290 
297  protected static function getStringLengthAt(string $msgData, int $pos)
298  {
299  return self::charLength(substr($msgData, $pos, 64));
300  }
301 
302 
311  public static function getByteValueAtOffsetPos(string $msgData, int $pos)
312  {
313  if (strlen($msgData) < $pos + 64) {
314  throw new \Exception('Not ennough data');
315  }
316  $lastByte = substr($msgData, $pos + 62, 2);
317  return self::byteLength($lastByte);
318  }
319 
329  protected static function getByteLength(int $l)
330  {
331  return (new EthQ($l, ['abi' => 'uint256']))->hexVal();
332  }
333 
334 
340  protected static function getLengthInByte(string $str, int $currentPos)
341  {
342  // (Total length - (Current position + length byte)) / charsPerByte.
343  return (strlen($str) - $currentPos + 64) / 2;
344  }
345 
350  protected static function paddedLength(int $length)
351  {
352  return $length + 64 - ($length % 64);
353  }
354 
355 
356  // protected static function isOutofBoundary(string $msgData, int $currentPos): bool {
357  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
358  // $X = (strlen($msgData) <= $val);
359  // return $X;
360  // }
361  //
362  // protected static function isListBiggerThan55Bytes(string $msgData, int $currentPos): bool {
363  // // return msgData[currentPosition] > OFFSET_LONG_LIST;
364  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
365  // $X = ($val > self::OFFSET_LONG_LIST && strlen($msgData) >= $val);
366  // return $X;
367  // }
368  //
369  // protected static function isListLessThan55Bytes(string $msgData, int $currentPos): bool {
370  // // return msgData[currentPosition] >= OFFSET_SHORT_LIST && msgData[currentPosition] <= OFFSET_LONG_LIST;
371  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
372  // $X = ($val >= self::OFFSET_SHORT_LIST && $val <= self::OFFSET_LONG_LIST);
373  // return $X;
374  // }
375  // protected static function isItemBiggerThan55Bytes(string $msgData, int $currentPos): bool {
376  // // return msgData[currentPosition] > OFFSET_LONG_ITEM && msgData[currentPosition] < OFFSET_SHORT_LIST;
377  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
378  // $X = ($val > self::OFFSET_LONG_ITEM && $val < self::OFFSET_SHORT_LIST);
379  // return $X;
380  // }
381  // protected static function isItemLessThan55Bytes(string $msgData, int $currentPos): bool {
382  // // return msgData[currentPosition] > OFFSET_SHORT_ITEM && msgData[currentPosition] <= OFFSET_LONG_ITEM;
383  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
384  // $X = ($val > self::OFFSET_SHORT_ITEM);
385  // return $X;
386  // }
387  // protected static function isNullItem(string $msgData, int $currentPos): bool {
388  // // return msgData[currentPosition] == OFFSET_SHORT_ITEM;
389  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
390  // $X = ($val === self::OFFSET_SHORT_ITEM);
391  // return $X;
392  //
393  // }
394  // protected static function isSigleByteItem(string $msgData, int $currentPos): bool {
395  // // return msgData[currentPosition] < OFFSET_SHORT_ITEM;
396  // $val = self::getByteValueAtOffsetPos($msgData, $currentPos);
397  // $X = ($val < self::OFFSET_SHORT_ITEM);
398  // return $X;
399  // }
400 }
static getByteValueAtOffsetPos(string $msgData, int $pos)
Definition: Rlp.php:311
const PREF_SELF_CONTAINED
Definition: Rlp.php:25
const OFFSET_SHORT_ITEM
Definition: Rlp.php:26
const OFFSET_LONG_LIST
Definition: Rlp.php:29
static getByteLength(int $l)
Definition: Rlp.php:329
const OFFSET_SHORT_LIST
Definition: Rlp.php:28
static paddedLength(int $length)
Definition: Rlp.php:350
const THRESHOLD_LONG
Definition: Rlp.php:24
static getByteValueAt(string $msgData, int $pos)
Definition: Rlp.php:285
static decode(string $hexVal)
Definition: Rlp.php:100
static getStringLengthAt(string $msgData, int $pos)
Definition: Rlp.php:297
const OFFSET_LONG_ITEM
Definition: Rlp.php:27
static encode(string $val)
Definition: Rlp.php:43
static processSingleByteItem(string $msgData, RLPCollection $rlpCollection, int $currentPos)
Definition: Rlp.php:257
static getLengthInByte(string $str, int $currentPos)
Definition: Rlp.php:340