diff --git a/composer.json b/composer.json index 9a79c2fa..72e36c34 100755 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "vendor/bin/php-cs-fixer fix" ], "test": [ - "./vendor/bin/pest --coverage --min=96 --coverage-html=.coverage --coverage-clover=coverage.xml" + "./vendor/bin/pest --coverage --min=100 --coverage-html=.coverage --coverage-clover=coverage.xml" ] }, "minimum-stability": "dev", diff --git a/phpunit.xml b/phpunit.xml index 2fd207d0..3f933cbd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,11 +1,13 @@ - + - - ./tests/Analysis - - - ./tests/Unit + + ./tests diff --git a/src/ByteBuffer/Concerns/Writes/UnsignedInteger.php b/src/ByteBuffer/Concerns/Writes/UnsignedInteger.php index db9f8d18..31d2b6d5 100644 --- a/src/ByteBuffer/Concerns/Writes/UnsignedInteger.php +++ b/src/ByteBuffer/Concerns/Writes/UnsignedInteger.php @@ -75,7 +75,7 @@ public function writeUInt64(int $value, int $offset = 0): self public function writeUInt256($value, int $offset = 0): self { // Convert the value to a GMP object for handling large numbers - if (is_numeric($value) || is_string($value)) { + if (is_numeric($value)) { $gmpValue = gmp_init($value); } elseif ($value instanceof \GMP) { $gmpValue = $value; diff --git a/src/Networks/AbstractNetwork.php b/src/Networks/AbstractNetwork.php index e1310cae..0d94bb39 100644 --- a/src/Networks/AbstractNetwork.php +++ b/src/Networks/AbstractNetwork.php @@ -14,17 +14,9 @@ abstract class AbstractNetwork extends Network implements NetworkInterface * @see Network::$base58PrefixMap */ protected $base58PrefixMap = [ - self::BASE58_WIF => 'aa', // 170 + self::BASE58_WIF => 'aa', // 170 ]; - /** - * {@inheritdoc} - */ - public static function __callStatic(string $method, array $args) - { - return static::factory()->{$method}(...$args); - } - /** * Create a new network instance. * diff --git a/tests/Unit/ByteBuffer/ByteBufferTest.php b/tests/Unit/ByteBuffer/ByteBufferTest.php index 5bebcecf..3d670635 100644 --- a/tests/Unit/ByteBuffer/ByteBufferTest.php +++ b/tests/Unit/ByteBuffer/ByteBufferTest.php @@ -177,6 +177,13 @@ expect($buffer->internalSize())->toBe(4 + 11); }); +it('should fill the buffer starting from a different start point', function () { + $buffer = ByteBuffer::new('hello'); + $buffer->fill(11, 4); + + expect($buffer->internalSize())->toBe(4 + 11); +}); + it('should flip the buffer contents', function () { $buffer = ByteBuffer::new('Hello World'); $buffer->flip(); diff --git a/tests/Unit/ByteBuffer/Concerns/Reads/UnsignedIntegerTest.php b/tests/Unit/ByteBuffer/Concerns/Reads/UnsignedIntegerTest.php index b86e4421..a5c3d7ff 100644 --- a/tests/Unit/ByteBuffer/Concerns/Reads/UnsignedIntegerTest.php +++ b/tests/Unit/ByteBuffer/Concerns/Reads/UnsignedIntegerTest.php @@ -71,3 +71,13 @@ expect($buffer->readULong())->toBe(64); }); + +test('it should read uint256', function () { + // 256-bit unsigned integer (32 bytes) + $value = '1157920892373161954235709850086879078532699846656405640323232344'; // max uint256 + $buffer = ByteBuffer::new(0); + $buffer->writeUInt256($value); + $buffer->position(0); + + expect($buffer->readUInt256())->toBe($value); +}); diff --git a/tests/Unit/ByteBuffer/Concerns/Writes/UnsignedIntegerTest.php b/tests/Unit/ByteBuffer/Concerns/Writes/UnsignedIntegerTest.php index 645797f6..b6db0e4c 100644 --- a/tests/Unit/ByteBuffer/Concerns/Writes/UnsignedIntegerTest.php +++ b/tests/Unit/ByteBuffer/Concerns/Writes/UnsignedIntegerTest.php @@ -63,3 +63,35 @@ expect($buffer->internalSize())->toBe(8); }); + +test('it should write uint256', function () { + // 256-bit unsigned integer (32 bytes) + $value = '1157920892373161954235709850086879078532699846656405640323232344'; // max uint256 + $buffer = ByteBuffer::new(0); + $buffer->writeUInt256($value); + + expect($buffer->internalSize())->toBe(32); +}); + +test('it should write uint256 gmp value', function () { + // 256-bit unsigned integer (32 bytes) + $value = gmp_init('1157920892373161954235709850086879078532699846656405640323232344'); // max uint256 + $buffer = ByteBuffer::new(0); + $buffer->writeUInt256($value); + + expect($buffer->internalSize())->toBe(32); +}); + +test('it should throw exception when writing invalid uint256', function () { + // 256-bit unsigned integer (32 bytes) + $value = 'asd'; + $buffer = ByteBuffer::new(0); + $buffer->writeUInt256($value); +})->throws(InvalidArgumentException::class, 'The value must be a numeric string, integer, or GMP object.'); + +test('it should throw exception when writing uint256 which is too long', function () { + // 256-bit unsigned integer (32 bytes) + $value = '1157920892373161954235709850086879078532699846656405640323232344444411579208923731619542357098500868790785326998466564056403232323444444'; + $buffer = ByteBuffer::new(0); + $buffer->writeUInt256($value); +})->throws(InvalidArgumentException::class, 'The value must fit into 256 bits.'); diff --git a/tests/Unit/Enums/AbiFunctionTest.php b/tests/Unit/Enums/AbiFunctionTest.php new file mode 100644 index 00000000..a6e73ee2 --- /dev/null +++ b/tests/Unit/Enums/AbiFunctionTest.php @@ -0,0 +1,24 @@ +transactionClass())->toEqual($class); +})->with([ + 'Vote' => ['VOTE', Vote::class], + 'Unvote' => ['UNVOTE', Unvote::class], + 'ValidatorRegistration' => ['VALIDATOR_REGISTRATION', ValidatorRegistration::class], + 'ValidatorResignation' => ['VALIDATOR_RESIGNATION', ValidatorResignation::class], + 'UsernameRegistration' => ['USERNAME_REGISTRATION', UsernameRegistration::class], + 'UsernameResignation' => ['USERNAME_RESIGNATION', UsernameResignation::class], + 'Multipayment' => ['MULTIPAYMENT', Multipayment::class], +]); diff --git a/tests/Unit/Identities/AddressTest.php b/tests/Unit/Identities/AddressTest.php index 240d0c72..091b62ce 100644 --- a/tests/Unit/Identities/AddressTest.php +++ b/tests/Unit/Identities/AddressTest.php @@ -38,3 +38,17 @@ expect($actual)->toBe($fixture['data']['address']); }); + +it('should validate an address', function () { + $fixture = $this->getFixture('identity'); + + $actual = Address::validate($fixture['data']['address']); + + expect($actual)->toBeTrue(); +}); + +it('should return false for an invalid address', function () { + $actual = Address::validate('invalid-address'); + + expect($actual)->toBeFalse(); +}); diff --git a/tests/Unit/Transactions/Builder/ValidatorRegistrationBuilderTest.php b/tests/Unit/Transactions/Builder/ValidatorRegistrationBuilderTest.php index 2a02dc65..b297f581 100644 --- a/tests/Unit/Transactions/Builder/ValidatorRegistrationBuilderTest.php +++ b/tests/Unit/Transactions/Builder/ValidatorRegistrationBuilderTest.php @@ -42,6 +42,13 @@ expect((string) $builder)->toBe($builder->toJson()); }); +it('should set value on the transaction', function () { + $builder = ValidatorRegistrationBuilder::new() + ->value(UnitConverter::parseUnits(10, 'ark')); + + expect((string) $builder->transaction->data['value'])->toBe('10000000000000000000'); +}); + it('should convert to an array', function () { $fixture = $this->getTransactionFixture('evm_call', 'unvote'); diff --git a/tests/Unit/Transactions/DeserializerTest.php b/tests/Unit/Transactions/DeserializerTest.php index 11ef54f2..93a4e374 100644 --- a/tests/Unit/Transactions/DeserializerTest.php +++ b/tests/Unit/Transactions/DeserializerTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use ArkEcosystem\Crypto\Transactions\Deserializer; use ArkEcosystem\Crypto\Transactions\Types\EvmCall; use ArkEcosystem\Crypto\Transactions\Types\Multipayment; use ArkEcosystem\Crypto\Transactions\Types\Transfer; @@ -88,3 +89,45 @@ expect($transaction)->toBeInstanceOf(Multipayment::class); }); + +it('should use ByteBuffer::fromHex when there is no null-byte in the string', function () { + // The string does not contain a null-byte + $hexString = 'abcdef1234567890'; + $deserializer = new Deserializer($hexString); + + // Use reflection to access the private buffer property + $reflection = new ReflectionClass($deserializer); + $bufferProperty = $reflection->getProperty('buffer'); + $bufferProperty->setAccessible(true); + $buffer = $bufferProperty->getValue($deserializer); + + // The buffer should be an instance of ByteBuffer + expect($buffer)->toBeInstanceOf(ArkEcosystem\Crypto\ByteBuffer\ByteBuffer::class); + + // The buffer should contain the hex string (converted to binary) + expect($buffer->toString('hex'))->toContain($hexString); +}); + +it('should use ByteBuffer::fromBinary when there is a null-byte in the string', function () { + // The string contains a null-byte + $binaryString = "abc\0def"; // hex: 61626300646566 + $hexString = '61626300646566'; + $deserializer = new Deserializer($binaryString); + + // Use reflection to access the private buffer property + $reflection = new ReflectionClass($deserializer); + $bufferProperty = $reflection->getProperty('buffer'); + $bufferProperty->setAccessible(true); + $buffer = $bufferProperty->getValue($deserializer); + + // The buffer should be an instance of ByteBuffer + expect($buffer)->toBeInstanceOf(ArkEcosystem\Crypto\ByteBuffer\ByteBuffer::class); + + // The buffer should contain the binary string + expect($buffer->toString('hex'))->toBe($hexString); +}); + +it('should return null if no data value in transaction data', function () { + expect(Deserializer::decodePayload([]))->toBeNull(); + expect(Deserializer::decodePayload(['data' => '']))->toBeNull(); +}); diff --git a/tests/Unit/Utils/AbiEncoderTest.php b/tests/Unit/Utils/AbiEncoderTest.php index e812741c..39cd8cb4 100644 --- a/tests/Unit/Utils/AbiEncoderTest.php +++ b/tests/Unit/Utils/AbiEncoderTest.php @@ -317,6 +317,24 @@ function testPrivateMethod(string $methodName, &$object): ReflectionMethod testPrivateMethod('encodeTuple', $object)->invokeArgs($object, [$tuple, $param]); })->throws(Exception::class, 'Tuple value missing component: from'); +it('should error when encoding a tuple with unnamed component', function () { + $tuple = [ + 'from' => '0xb693449AdDa7EFc015D87944EAE8b7C37EB1690A', + ]; + + $param = [ + 'name' => 'recipients', + 'type' => 'address', + 'components' => [ + [ + 'type' => 'address', + ], + ], + ]; + + testPrivateMethod('encodeTuple', $object)->invokeArgs($object, [$tuple, $param]); +})->throws(Exception::class, 'Tuple component missing name'); + it('should error for missing function name when preparing function data', function () { $param = [ ...json_decode(file_get_contents(dirname(dirname(dirname(__DIR__))).'/src/Utils/Abi/json/Abi.Consensus.json'), true), diff --git a/tests/Unit/Utils/TransactionUtilsTest.php b/tests/Unit/Utils/TransactionUtilsTest.php index 51d76d68..e43d93de 100644 --- a/tests/Unit/Utils/TransactionUtilsTest.php +++ b/tests/Unit/Utils/TransactionUtilsTest.php @@ -35,7 +35,8 @@ it('should handle string data starting with 0x', function () { $fixture = $this->getTransactionFixture('evm_call', 'username-resignation'); - $fixture['data']['nonce'] = '0x'.dechex((int) $fixture['data']['nonce']); + $fixture['data']['gasPrice'] = '0x'.dechex((int) $fixture['data']['gasPrice']); + $fixture['data']['nonce'] = '0x'.dechex((int) $fixture['data']['nonce']); $transaction = TransactionUtils::toBuffer($fixture['data']);