From 9b58df39aa654311c920440d07bf05533600c37d Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:20:54 +0100 Subject: [PATCH 1/4] handle hex2bin exception instead of relying on returning false --- src/Utils/Abi/ArgumentDecoder.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Utils/Abi/ArgumentDecoder.php b/src/Utils/Abi/ArgumentDecoder.php index eac3fbc1..3efc708e 100644 --- a/src/Utils/Abi/ArgumentDecoder.php +++ b/src/Utils/Abi/ArgumentDecoder.php @@ -12,7 +12,13 @@ final class ArgumentDecoder public function __construct(string $bytes) { - $bytes = hex2bin($bytes); + try { + $bytes = hex2bin($bytes); + } catch (\Throwable $e) { + // Handle the case where hex2bin fails, e.g., invalid hex string + $bytes = false; + } + if ($bytes === false) { $bytes = ''; } From fcb9b68e8db94b6d5b1bcf26f11ee813a526821e Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:21:37 +0100 Subject: [PATCH 2/4] remove legacy message public key formatting & handling --- src/Utils/Message.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Utils/Message.php b/src/Utils/Message.php index 3e1ef91a..311f0dfb 100644 --- a/src/Utils/Message.php +++ b/src/Utils/Message.php @@ -44,16 +44,11 @@ class Message */ public function __construct(object $message) { - if (property_exists($message, 'publickey')) { - $this->publicKey = $message->publickey; - } elseif (property_exists($message, 'publicKey')) { - $this->publicKey = $message->publicKey; - } elseif (property_exists($message, 'signatory')) { - $this->publicKey = $message->signatory; - } else { + if (! property_exists($message, 'publicKey')) { throw new InvalidArgumentException('The given message did not contain a valid public key.'); } + $this->publicKey = $message->publicKey; $this->signature = $message->signature; $this->message = $message->message; } @@ -113,7 +108,7 @@ public static function sign(string $message, string $passphrase): self $v = dechex($signature->getRecoveryId() + 27); return static::new([ - 'publickey' => $privateKey->publicKey, + 'publicKey' => $privateKey->publicKey, 'signature' => $r.$s.$v, 'message' => $message, ]); @@ -144,7 +139,7 @@ public function verify(): bool public function toArray(): array { return [ - 'publickey' => $this->publicKey, + 'publicKey' => $this->publicKey, 'signature' => $this->signature, 'message' => $this->message, ]; From 70877d232e94ff41ef1f2053d3d63d270ed15b0e Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Wed, 11 Jun 2025 17:21:46 +0100 Subject: [PATCH 3/4] test: remaining utils coverage --- tests/Unit/Utils/Abi/ArgumentDecoderTest.php | 18 +++++ tests/Unit/Utils/AddressTest.php | 17 +++++ tests/Unit/Utils/MessageTest.php | 8 ++ tests/Unit/Utils/TransactionUtilsTest.php | 80 ++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 tests/Unit/Utils/TransactionUtilsTest.php diff --git a/tests/Unit/Utils/Abi/ArgumentDecoderTest.php b/tests/Unit/Utils/Abi/ArgumentDecoderTest.php index 0c388b50..638cc0dd 100644 --- a/tests/Unit/Utils/Abi/ArgumentDecoderTest.php +++ b/tests/Unit/Utils/Abi/ArgumentDecoderTest.php @@ -19,6 +19,15 @@ expect($decoder->decodeAddress())->toBe($expected); }); +it('should decode a string', function () { + $payload = '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000474657374'; + $expected = 'test'; + + $decoder = new ArgumentDecoder($payload); + + expect($decoder->decodeString())->toBe($expected); +}); + it('should decode unsigned int', function () { $payload = '000000000000000000000000000000000000000000000000016345785d8a0000'; $expected = '100000000000000000'; @@ -54,3 +63,12 @@ expect($decoder->decodeBool())->toBe($expected); }); + +it('should handle issue converting hex to binary', function () { + $decoder = new ArgumentDecoder('invalid'); + + $reflectionProperty = new \ReflectionProperty(ArgumentDecoder::class, 'bytes'); + $reflectionProperty->setAccessible(true); + + expect($reflectionProperty->getValue($decoder))->toBe(''); +}); diff --git a/tests/Unit/Utils/AddressTest.php b/tests/Unit/Utils/AddressTest.php index 6f0ba7bb..b70e8daa 100644 --- a/tests/Unit/Utils/AddressTest.php +++ b/tests/Unit/Utils/AddressTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use ArkEcosystem\Crypto\ByteBuffer\ByteBuffer; use ArkEcosystem\Crypto\Utils\Address as TestClass; test('it should validate the address', function () { @@ -17,3 +18,19 @@ expect($actual)->toBeFalse(); }); + +it('should convert to hex string', function () { + $fixture = $this->getFixture('identity'); + + $actual = TestClass::toBufferHexString($fixture['data']['address']); + + expect($actual)->toBe(substr($fixture['data']['address'], 2)); +}); + +it('should extract address from a byte buffer', function () { + $fixture = $this->getFixture('identity'); + + $actual = TestClass::fromByteBuffer(ByteBuffer::fromHex(substr($fixture['data']['address'], 2))); + + expect($actual)->toBe($fixture['data']['address']); +}); diff --git a/tests/Unit/Utils/MessageTest.php b/tests/Unit/Utils/MessageTest.php index 2ce906b2..35093fee 100644 --- a/tests/Unit/Utils/MessageTest.php +++ b/tests/Unit/Utils/MessageTest.php @@ -34,6 +34,14 @@ expect($message->message)->toBe($fixture['message']); }); +test('it should throw if no public key is provided', function () { + $fixture = $this->getFixture('message-sign'); + + unset($fixture['publicKey']); + + Message::new($fixture); +})->throws(InvalidArgumentException::class, 'The given message did not contain a valid public key.'); + test('it should create a message from a string', function () { $fixture = $this->getFixture('message-sign'); diff --git a/tests/Unit/Utils/TransactionUtilsTest.php b/tests/Unit/Utils/TransactionUtilsTest.php new file mode 100644 index 00000000..eb59ce6a --- /dev/null +++ b/tests/Unit/Utils/TransactionUtilsTest.php @@ -0,0 +1,80 @@ +getTransactionFixture('evm_call', 'username-resignation'); + + $transaction = TransactionUtils::toBuffer($fixture['data']); + + expect($transaction->getHex())->toBe($fixture['serialized']); +}); + +it('should convert a transaction to a buffer when data starts with 0x', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $fixture['data']['data'] = '0x' . $fixture['data']['data']; + + $transaction = TransactionUtils::toBuffer($fixture['data']); + + expect($transaction->getHex())->toBe($fixture['serialized']); +}); + +it('should get the hash for a transaction', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $transaction = TransactionUtils::toHash($fixture['data']); + + expect($transaction->getHex())->toBe($fixture['data']['hash']); +}); + +it('should handle string data starting with 0x', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $fixture['data']['gasPrice'] = '0x'.dechex($fixture['data']['gasPrice']); + + $transaction = TransactionUtils::toBuffer($fixture['data']); + + expect($transaction->getHex())->toBe($fixture['serialized']); +}); + +it('should handle BigDecimal value', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $fixture['data']['gasPrice'] = BigDecimal::of($fixture['data']['gasPrice']); + + $transaction = TransactionUtils::toBuffer($fixture['data']); + + expect($transaction->getHex())->toBe($fixture['serialized']); +}); + +it('should handle zero BigDecimal value', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $fixture['data']['gasPrice'] = BigDecimal::zero(); + + $transaction = TransactionUtils::toBuffer($fixture['data'], true); + + $decoded = RlpDecoder::decode('0x'.substr($transaction->getHex(), 2)); + + expect($decoded[3])->toBe('0x'); +}); + +it('should handle unknown value value', function () { + $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); + + $fixture['data']['gasPrice'] = 123.456; + + $transaction = TransactionUtils::toBuffer($fixture['data'], true); + + $decoded = RlpDecoder::decode('0x'.substr($transaction->getHex(), 2)); + + expect($decoded[3])->toBe('0x'); +}); + +// toBuffer +// toHash From df46cb1981806bd870e5a201a73a023ee67c21b2 Mon Sep 17 00:00:00 2001 From: alexbarnsley Date: Wed, 11 Jun 2025 16:22:21 +0000 Subject: [PATCH 4/4] style: resolve style guide violations --- tests/Unit/Utils/Abi/ArgumentDecoderTest.php | 2 +- tests/Unit/Utils/TransactionUtilsTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Unit/Utils/Abi/ArgumentDecoderTest.php b/tests/Unit/Utils/Abi/ArgumentDecoderTest.php index 638cc0dd..df0d7cb7 100644 --- a/tests/Unit/Utils/Abi/ArgumentDecoderTest.php +++ b/tests/Unit/Utils/Abi/ArgumentDecoderTest.php @@ -20,7 +20,7 @@ }); it('should decode a string', function () { - $payload = '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000474657374'; + $payload = '0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000474657374'; $expected = 'test'; $decoder = new ArgumentDecoder($payload); diff --git a/tests/Unit/Utils/TransactionUtilsTest.php b/tests/Unit/Utils/TransactionUtilsTest.php index eb59ce6a..c45005a7 100644 --- a/tests/Unit/Utils/TransactionUtilsTest.php +++ b/tests/Unit/Utils/TransactionUtilsTest.php @@ -1,10 +1,10 @@ getTransactionFixture('evm_call', 'username-resignation'); @@ -17,7 +17,7 @@ it('should convert a transaction to a buffer when data starts with 0x', function () { $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); - $fixture['data']['data'] = '0x' . $fixture['data']['data']; + $fixture['data']['data'] = '0x'.$fixture['data']['data']; $transaction = TransactionUtils::toBuffer($fixture['data']);