67 lines
1.7 KiB
PHP
67 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Chain;
|
|
|
|
/**
|
|
* Minimal RLP encoder for legacy Ethereum transactions.
|
|
*/
|
|
final class EthRlp
|
|
{
|
|
/**
|
|
* @param list<string|list<mixed>> $list binary strings or nested lists
|
|
*/
|
|
public static function encodeList(array $list): string
|
|
{
|
|
$payload = '';
|
|
foreach ($list as $item) {
|
|
$payload .= is_array($item) ? self::encodeList($item) : self::encodeString($item);
|
|
}
|
|
|
|
return self::encodeLength(strlen($payload), 0xc0).$payload;
|
|
}
|
|
|
|
public static function encodeString(string $input): string
|
|
{
|
|
$len = strlen($input);
|
|
if ($len === 1 && ord($input) < 0x80) {
|
|
return $input;
|
|
}
|
|
|
|
return self::encodeLength($len, 0x80).$input;
|
|
}
|
|
|
|
/** Integer → minimal big-endian binary (empty for zero). */
|
|
public static function intToBin(string|int $value): string
|
|
{
|
|
if (is_int($value)) {
|
|
$value = (string) $value;
|
|
}
|
|
if (! preg_match('/^\d+$/', $value)) {
|
|
throw new \InvalidArgumentException('Invalid integer');
|
|
}
|
|
if (bccomp($value, '0') === 0) {
|
|
return '';
|
|
}
|
|
$hex = gmp_strval(gmp_init($value, 10), 16);
|
|
if (strlen($hex) % 2 !== 0) {
|
|
$hex = '0'.$hex;
|
|
}
|
|
|
|
return hex2bin($hex) ?: '';
|
|
}
|
|
|
|
private static function encodeLength(int $len, int $offset): string
|
|
{
|
|
if ($len < 56) {
|
|
return chr($len + $offset);
|
|
}
|
|
$hex = dechex($len);
|
|
if (strlen($hex) % 2 !== 0) {
|
|
$hex = '0'.$hex;
|
|
}
|
|
$bin = hex2bin($hex) ?: '';
|
|
|
|
return chr(strlen($bin) + $offset + 55).$bin;
|
|
}
|
|
}
|