Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Add DidSolProgram feature #53

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ vendor
.php_cs.cache
.phpunit.result.cache
.idea
.DS_Store
.env
.history
.vscode
coverage
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ $accountInfoStatusCode = $accountInfoResponse->getStatusCode();

### Transactions

Here is working example of sending a transfer instruction to the Solana blockchain:
Here is working example of sending a transfer instruction to the Solana blockchain, you may overrride the Endpoint with a custom RPC endpoint.:

```php
$client = new SolanaRpcClient(SolanaRpcClient::DEVNET_ENDPOINT);
Expand All @@ -71,6 +71,46 @@ $transaction->add($instruction);
$txHash = $connection->sendTransaction($transaction, $fromPublicKey);
```

### Borsh Derialize & Deserialize

For Borsh serialization/deseralization to work, a class::SCHEMA object reflecting the Program Structs, based on the program IDL must be passed or defined. e.g.

```php
class DidData
{

use BorshDeserializable;


public const SCHEMA = [
VerificationMethodStruct::class => VerificationMethodStruct::SCHEMA[VerificationMethodStruct::class],
ServiceStruct::class => ServiceStruct::SCHEMA[ServiceStruct::class],
self::class => [
'kind' => 'struct',
'fields' => [
['offset', 'u64'],
['version', 'u8'],
['bump', 'u8'],
['nonce', 'u64'],
['initialVerificationMethod', 'string'],
['flags', 'u16'],
['methodType', 'u8'],
['keyData', ['u8']],
['verificationMethods', [VerificationMethodStruct::class]],
['services', [ServiceStruct::class]],
['nativeControllers', ['pubKey']],
['otherControllers', ['string']],
],
],
];

public static function fromBuffer(array $buffer): self
{
return Borsh::deserialize(self::SCHEMA, self::class, $buffer);
}
}
```

Note: This project is in alpha, the code to generate instructions is still being worked on `$instruction = SystemProgram::abc()`

## Roadmap
Expand Down
35 changes: 35 additions & 0 deletions src/Accounts/Did/ServiceStruct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Tighten\SolanaPhpSdk\Accounts\Did;

use Tighten\SolanaPhpSdk\Borsh;

/**
* Class ServiceStruct
*
* This class represents a service for a Decentralized Identifier (DID) account.
* It provides methods for creating and managing services, signing and verifying messages, and other related operations.
* @version 1.0
* @package Tighten\SolanaPhpSdk\Accounts\Did
* @license MIT
* @author Eduardo Chongkan
* @link https://chongkan.com
* @see https://github.com/identity-com/sol-did/tree/develop/sol-did/client/packages/idl
* @see https://explorer.solana.com/address/didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc/anchor-program?cluster=devnet
*/

class ServiceStruct
{
use Borsh\BorshDeserializable;

public const SCHEMA = [
self::class => [
'kind' => 'struct',
'fields' => [
['fragment', 'string'],
['serviceType', 'string'],
['serviceEndpoint', 'string']
],
],
];
}
35 changes: 35 additions & 0 deletions src/Accounts/Did/VerificationMethodStruct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Tighten\SolanaPhpSdk\Accounts\Did;

use Tighten\SolanaPhpSdk\Borsh;

/**
* Class VerificationMethodStruct
*
* This class represents a verification method for a Decentralized Identifier (DID) account.
* It provides methods for creating and managing verification methods, signing and verifying messages, and other related operations.
* @version 1.0
* @package Tighten\SolanaPhpSdk\Accounts\Did
* @license MIT
* @author Eduardo Chongkan
* @link https://chongkan.com
* @see https://github.com/identity-com/sol-did/tree/develop/sol-did/client/packages/idl
* @see https://explorer.solana.com/address/didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc/anchor-program?cluster=devnet
*/
class VerificationMethodStruct
{
use Borsh\BorshDeserializable;

public const SCHEMA = [
self::class => [
'kind' => 'struct',
'fields' => [
['fragment', 'string'],
['flags', 'u16'],
['methodType', 'u8'],
['keyData', 'bytes']
],
],
];
}
56 changes: 56 additions & 0 deletions src/Accounts/DidData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Tighten\SolanaPhpSdk\Accounts;

use Tighten\SolanaPhpSdk\Accounts\Did\VerificationMethodStruct;
use Tighten\SolanaPhpSdk\Accounts\Did\ServiceStruct;
use Tighten\SolanaPhpSdk\Borsh\Borsh;
use Tighten\SolanaPhpSdk\Borsh\BorshDeserializable;

/**
* Class DidData
*
* This class represents a Decentralized Identifier (DID) account.
* It provides methods for creating and managing DID accounts, signing and verifying messages, and other related operations.
* @version 1.0
* @package Tighten\SolanaPhpSdk\Accounts
* @license MIT
* @author Eduardo Chongkan
* @link https://chongkan.com
* @see https://github.com/identity-com/sol-did/tree/develop/sol-did/client/packages/idl
* @see https://explorer.solana.com/address/didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc/anchor-program?cluster=devnet
*/

class DidData
{

use BorshDeserializable;


public const SCHEMA = [
VerificationMethodStruct::class => VerificationMethodStruct::SCHEMA[VerificationMethodStruct::class],
ServiceStruct::class => ServiceStruct::SCHEMA[ServiceStruct::class],
self::class => [
'kind' => 'struct',
'fields' => [
['offset', 'u64'],
['version', 'u8'],
['bump', 'u8'],
['nonce', 'u64'],
['initialVerificationMethod', 'string'],
['flags', 'u16'],
['methodType', 'u8'],
['keyData', ['u8']],
['verificationMethods', [VerificationMethodStruct::class]],
['services', [ServiceStruct::class]],
['nativeControllers', ['pubKey']],
['otherControllers', ['string']],
],
],
];

public static function fromBuffer(array $buffer): self
{
return Borsh::deserialize(self::SCHEMA, self::class, $buffer);
}
}
98 changes: 98 additions & 0 deletions src/Programs/DidSolProgram.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

namespace Tighten\SolanaPhpSdk\Programs;

use Tighten\SolanaPhpSdk\Program;
use Tighten\SolanaPhpSdk\Accounts\DidData;
use Tighten\SolanaPhpSdk\PublicKey;
use StephenHill\Base58;
use Tighten\SolanaPhpSdk\SolanaRpcClient;

/**
* Class DidSolProgram
*
* This class represents a program for interacting with the Solana blockchain using the DID (Decentralized Identifier) protocol.
* It provides methods for creating and managing DID accounts, signing and verifying messages, and other related operations.
* @version 1.0
* @package Tighten\SolanaPhpSdk\
* @license MIT
* @author Eduardo Chongkan
* @link https://chongkan.com
* @see https://github.com/identity-com/sol-did
*/

class DidSolProgram extends Program
{
public const DIDSOL_PROGRAM_ID = 'didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc';
public const DIDSOL_DEFAULT_SEED = 'did-account';

/**
* getDidDataAcccountInfo
*
* @param SolanaRpcClient $client
* @param string $base58SubjectPk The PK of the DID.
* @param bool $onlyAccData
* @return DidData|string
* @example DidSolProgram::getDidDataAcccountInfo($client, 'did:sol:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk', false);
*/
static function getDidDataAcccountInfo($client, $base58SubjectPk, $onlyAccData = true)
{
$pdaPublicKey = self::getDidDataAccountId($base58SubjectPk);

$accountInfoResponse = $client->call('getAccountInfo', [$pdaPublicKey, ["encoding" => "jsonParsed"]]);
$dataBase64 = $accountInfoResponse['value']['data'][0];

if (!$onlyAccData) {
return $dataBase64;
}


$didData = self::deserializeDidData($dataBase64);

return $didData;
}


/**
* getDidDataAccountId
*
* @param string $did 'did:sol:[cluster]....'
* @return string The base58 encoded public key of the DID data account
* @example DidSolProgram::getDidDataAccountId('did:sol:devnet:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk');
*/
static function getDidDataAccountId($base58SubjectPk)
{

$b58 = new Base58();

$seeds = array(self::DIDSOL_DEFAULT_SEED, $b58->decode($base58SubjectPk));
$pId = new PublicKey(self::DIDSOL_PROGRAM_ID);
$publicKey = PublicKey::findProgramAddress($seeds, $pId);

return $publicKey[0]->toBase58();
}

/**
* deserializeDidData
*
* @param string $dataBase64 The base64 encoded data of the DID data account
* @return DidData The deserialized DID data object
* @example DidSolProgram::deserializeDidData('TVjvjfsd7fMA/gAAAA...');
*/
static function deserializeDidData($dataBase64)
{

$base64String = base64_decode($dataBase64);
$uint8Array = array_values(unpack('C*', $base64String));
$didData = DidData::fromBuffer($uint8Array);

$keyData = $didData->keyData;

$binaryString = pack('C*', ...$keyData);

$b58 = new Base58();
$base58String = $b58->encode($binaryString);
$didData->keyData = $base58String;
return $didData;
}
}
52 changes: 52 additions & 0 deletions tests/Unit/DidSolProgramTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Tighten\SolanaPhpSdk\Tests\Unit;

use Tighten\SolanaPhpSdk\Account;
use Tighten\SolanaPhpSdk\Keypair;
use Tighten\SolanaPhpSdk\Tests\TestCase;
use Tighten\SolanaPhpSdk\Programs\DidSolProgram;
use Tighten\SolanaPhpSdk\SolanaRpcClient;

class DidSolProgramTest extends TestCase
{

public const ACC_DATA_SIZE = 158;
public const DID_ID = 'did:sol:devnet:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk';
public const DID_SUBJECT_PK = '3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk';
public const DID_ACCOUNT_ID = '2LA5JTs1cxFewfnXzVBpaFHpABBj1akR2aQzwDSovwCg';
public const DID_DATA = 'TVjvjfsd7fMA/gAAAAAAAAAABwAAAGRlZmF1bHRIAAAgAAAAIkrqC+g88eamANb3tU6OiBJW21IjBWP85MhI4XKkOscAAAAAAQAAAAUAAABhZ2VudAwAAABBZ2VudFNlcnZpY2UtAAAAaHR0cHM6Ly9hdHRlc3R0by1icmVlemUtdnVlLnRlc3QvLndlbGwta25vd24vAAAAAAAAAAA=';
/** @test */
public function it_deserializes_diddata()
{
$base64Data = self::DID_DATA;
$didData = DidSolProgram::deserializeDidData($base64Data);

$this->assertEquals($didData->keyData, self::DID_SUBJECT_PK);

}
/** @test */
public function it_gets_did_data_account_info()
{
$client = new SolanaRpcClient(SolanaRpcClient::DEVNET_ENDPOINT);
$accountInfoResponse = DidSolProgram::getDidDataAcccountInfo($client, self::DID_SUBJECT_PK, false);
$this->assertEquals($accountInfoResponse, self::DID_DATA);

}
/** @test */
public function it_gets_did_data_account_info_data()
{
$client = new SolanaRpcClient(SolanaRpcClient::DEVNET_ENDPOINT);
$didData = DidSolProgram::getDidDataAcccountInfo($client, self::DID_SUBJECT_PK,);
$this->assertEquals($didData->keyData, self::DID_SUBJECT_PK);

}
/** @test */
public function it_gets_did_data_account_id()
{

$didId = DidSolProgram::getDidDataAccountId( self::DID_SUBJECT_PK,);
$this->assertEquals($didId, self::DID_ACCOUNT_ID);

}
}