Skip to content

Commit

Permalink
update transfer json and rlp encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
alfonsobries committed Jan 23, 2025
1 parent e7f0c56 commit 86a1de7
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 72 deletions.
162 changes: 101 additions & 61 deletions src/Utils/RlpEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,146 @@

namespace ArkEcosystem\Crypto\Utils;

use InvalidArgumentException;

class RlpEncoder
{
public static function decode(string $data): string|array
public static function decode(string $data): mixed
{
$decoded = self::_decode($data, 0);
$bytes = self::getBytes($data, 'data');
$decoded = self::_decode($bytes, 0);

assert($decoded['consumed'] === strlen($data), 'unexpected junk after rlp payload');
if ($decoded['consumed'] !== count($bytes)) {
throw new InvalidArgumentException('unexpected junk after RLP payload');
}

return $decoded['result'];
}

private static function hexlifyByte(int $value): string
private static function getBytes(string $value, string $name = 'value'): array
{
$result = dechex($value);
while (strlen($result) < 2) {
$result = '0'.$result;
if (preg_match('/^0x(?:[0-9a-fA-F]{2})*$/', $value)) {
$hex = substr($value, 2);
$length = strlen($hex) / 2;
$bytes = [];

for ($i = 0; $i < $length; $i++) {
$pair = substr($hex, $i * 2, 2);
$bytes[] = hexdec($pair);
}
return $bytes;
}

return '0x'.$result;
throw new InvalidArgumentException(
sprintf('Invalid BytesLike value for "%s": %s', $name, $value)
);
}

private static function _decodeChildren(string $data, int $offset, int $childOffset, int $length): array
private static function hexlify(array $data): string
{
$result = [];

while ($childOffset < $offset + 1 + $length) {
$decoded = self::_decode($data, $childOffset);

$result[] = $decoded['result'];

$childOffset += $decoded['consumed'];
assert($childOffset <= $offset + 1 + $length, 'child data too short');
$hex = '';
foreach ($data as $byte) {
$hex .= sprintf('%02x', $byte);
}

var_dump($result);

return ['consumed' => ($childOffset - $offset), 'result' => $result];
return '0x' . $hex;
}

private static function hexlify(string $data): string
private static function hexlifyByte(int $value): string
{
return '0x'.bin2hex($data);
return '0x' . sprintf('%02x', $value & 0xff);
}

private static function unarrayifyInteger(string $data, int $offset, int $length): int
private static function unarrayifyInteger(array $data, int $offset, int $length): int
{
$result = 0;
for ($i = 0; $i < $length; $i++) {
$result = ($result << 8) + ord($data[$offset + $i]);
$result = ($result << 8) + $data[$offset + $i];
}

return $result;
}

private static function _decode(string $data, int $offset): string|array
/**
* Decodes a list of consecutive RLP items within $length.
*/
private static function _decodeChildren(array $data, int $offset, int $childOffset, int $length): array
{
assert(strlen($data) !== 0, 'data too short');
$result = [];
$end = $offset + 1 + $length;

while ($childOffset < $end) {
$decoded = self::_decode($data, $childOffset);
$result[] = $decoded['result'];
$childOffset += $decoded['consumed'];

$checkOffset = function ($offset) use ($data) {
assert($offset <= strlen($data), 'data short segment too short');
};
if ($childOffset > $end) {
throw new InvalidArgumentException('child data too short or malformed');
}
}

var_dump($data[$offset], ord($data[$offset]), '', 0xf8, 0xc0, 0xb8, 0x80);
return [
'consumed' => 1 + $length,
'result' => $result
];
}

if (ord($data[$offset]) >= 0xf8) {
$lengthLength = ord($data[$offset]) - 0xf7;
$checkOffset($offset + 1 + $lengthLength);
private static function _decode(array $data, int $offset): array
{
self::checkOffset($offset, $data);

$prefix = $data[$offset];

if ($prefix >= 0xf8) {
$lengthLength = $prefix - 0xf7;
self::checkOffset($offset + $lengthLength, $data);

$length = self::unarrayifyInteger($data, $offset + 1, $lengthLength);
$checkOffset($offset + 1 + $lengthLength + $length);
self::checkOffset($offset + 1 + $lengthLength + $length - 1, $data);

return self::_decodeChildren($data, $offset, $offset + 1 + $lengthLength, $lengthLength + $length);
} elseif (ord($data[$offset]) >= 0xc0) {
$length = ord($data[$offset]) - 0xc0;
$checkOffset($offset + 1 + $length);

} elseif ($prefix >= 0xc0) {
$length = $prefix - 0xc0;
if ($length > 0) {
self::checkOffset($offset + 1 + $length - 1, $data);
}
return self::_decodeChildren($data, $offset, $offset + 1, $length);
} elseif (ord($data[$offset]) >= 0xb8) {
$lengthLength = ord($data[$offset]) - 0xb7;
$checkOffset($offset + 1 + $lengthLength);
} elseif ($prefix >= 0xb8) {
$lengthLength = $prefix - 0xb7;
self::checkOffset($offset + $lengthLength, $data);

$length = self::unarrayifyInteger($data, $offset + 1, $lengthLength);
$checkOffset($offset + 1 + $lengthLength + $length);

$result = self::hexlify(substr($data, $offset + 1 + $lengthLength, $length));

var_dump($result);

return ['consumed' => (1 + $lengthLength + $length), 'result' => $result];
} elseif (ord($data[$offset]) >= 0x80) {
$length = ord($data[$offset]) - 0x80;
$checkOffset($offset + 1 + $length);

$result = self::hexlify(substr($data, $offset + 1, $length));
if ($length > 0) {
self::checkOffset($offset + 1 + $lengthLength + $length - 1, $data);
}
$slice = array_slice($data, $offset + 1 + $lengthLength, $length);

return [
'consumed' => 1 + $lengthLength + $length,
'result' => self::hexlify($slice)
];
} elseif ($prefix >= 0x80) {
$length = $prefix - 0x80;
if ($length > 0) {
self::checkOffset($offset + 1 + $length - 1, $data);
}
$slice = array_slice($data, $offset + 1, $length);

return [
'consumed' => 1 + $length,
'result' => self::hexlify($slice)
];
}

var_dump($result);
return [
'consumed' => 1,
'result' => self::hexlifyByte($prefix)
];
}

return ['consumed' => (1 + $length), 'result' => $result];
private static function checkOffset(int $offset, array $data): void
{
// We allow offset == count($data) to handle zero-length items.
if ($offset > count($data)) {
throw new InvalidArgumentException('data short segment or out of range');
}

return ['consumed' => 1, 'result' => self::hexlifyByte(ord($data[$offset]))];
}
}
20 changes: 9 additions & 11 deletions tests/fixtures/transactions/evm_call/transfer.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
{
"data": {
"network": 30,
"nonce": "77",
"nonce": "0",
"gasPrice": 5,
"gasLimit": 21000,
"recipientAddress": "0xb693449adda7efc015d87944eae8b7c37eb1690a",
"value": "100000000",
"recipientAddress": "0x6f0182a0cc707b055322ccf6d4cb6a5aff1aeb22",
"data": "",
"signature": "???",
"senderPublicKey": "03f25455408f9a7e6c6a056b121e68fbda98f3511d22e9ef27b0ebaf1ef9e4eabc",
"senderAddress": "0xfEAf2f24ba1205e9255d015DFaD8463c70D9A466",
"id": "c43f8786bf53d1e519f087b10d4ffb77871da17bcd4f5137e166792d7dffb78c",

"v": 27,
"r": "720decfb10c1d823323365c141b0f09369fe39ab18356f4e9826e96e30ce1916",
"s": "0461c4fa5df5899f6e681b53d9b94195bbcca7eac53d00096b0d9cde0c81c9f7"
"v": 28,
"r": "38264310b6a1985889858e5d2269d64cff19d4693d8f8880fe5f0878fa21087a",
"s": "2b1ff0c3abeba371dd5fb2e8568273de30832c5b142fc20313ce982d5fe848c7",
"senderPublicKey": "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
"senderAddress": "0x41aD2bc63A2059f9b623533d87fe99887D794847",
"id": "9e8584490bb11f2e511c90d564d4393ead7499d6feb5f977983ab01f09a640eb"
},
"serialized": "1e0c0000000000000005000000085200000000000000000000000000000000000000000000000000008ac7230489e800000107ac3e438719be72a9e2591bb6015f10e8af246800000000b3bc84c8caf1b75c18a78dde87df9f555161003d341eafad659ab672501185e413a26284c3c95056809c7d440c4ffab26179c538864c4d14534ebd5a961852bf01"
"serialized": "02f8661e80800582520894b693449adda7efc015d87944eae8b7c37eb1690a8405f5e10080c001a038264310b6a1985889858e5d2269d64cff19d4693d8f8880fe5f0878fa21087aa02b1ff0c3abeba371dd5fb2e8568273de30832c5b142fc20313ce982d5fe848c7"
}

0 comments on commit 86a1de7

Please sign in to comment.