Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
31.11% covered (danger)
31.11%
14 / 45
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
DidSolProgram
31.11% covered (danger)
31.11%
14 / 45
40.00% covered (danger)
40.00%
2 / 5
78.08
0.00% covered (danger)
0.00%
0 / 1
 getDidDataAcccountInfo
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getDidDataAccountId
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 deserializeDidData
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 parse
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 getRpcEndpointFromShortcut
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace Attestto\SolanaPhpSdk\Programs;
4
5use Attestto\SolanaPhpSdk\Exceptions\BaseSolanaPhpSdkException;
6use Attestto\SolanaPhpSdk\Program;
7use Attestto\SolanaPhpSdk\Accounts\DidData;
8use Attestto\SolanaPhpSdk\PublicKey;
9use StephenHill\Base58;
10use Attestto\SolanaPhpSdk\SolanaRpcClient;
11
12/**
13 * Class DidSolProgram - Work In Progress
14 * 
15 * This class represents a program for interacting with the Solana blockchain using the DID (Decentralized Identifier) protocol.
16 * It provides methods for creating and managing DID accounts, signing and verifying messages, and other related operations.
17 * @version 1.0
18 * @package Attestto\SolanaPhpSdk\
19 * @license MIT
20 * @author Eduardo Chongkan
21 * @link https://chongkan.com
22 * @see https://github.com/identity-com/sol-did
23 */
24
25class DidSolProgram extends Program
26{
27    public const DIDSOL_PROGRAM_ID = 'didso1Dpqpm4CsiCjzP766BGY89CAdD6ZBL68cRhFPc';
28    public const DIDSOL_DEFAULT_SEED = 'did-account';
29
30    /**
31     * getDidDataAcccountInfo
32     *
33     * @param SolanaRpcClient|string $client The RPC client or the custom RPC endpoint URL to use.
34     * @param string $base58SubjectPk The Public Key of the DID.
35     * @return string (JSON) The account info of the DID data account as it comes from the RPC
36     * @example DidSolProgram::getDidDataAcccountInfo($client, 'did:sol:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk', false);
37     */
38    static function getDidDataAcccountInfo($client, $base58SubjectPk)
39    {
40        $pdaPublicKey =  self::getDidDataAccountId($base58SubjectPk);
41        return $client->call('getAccountInfo', [$pdaPublicKey, ["encoding" => "jsonParsed"]])['value'];
42        // Data is always returned in base54 because it exceeds 128 bytes
43    }
44
45    /**
46     * getDidDataAccountId
47     *
48     * @param string $did 'did:sol:[cluster]....'
49     * @return string The base58 encoded public key of the DID data account
50     * @throws BaseSolanaPhpSdkException
51     * @example DidSolProgram::getDidDataAccountId('did:sol:devnet:3Js7k6xYQbvXv6qUYLapYV7Sptfg37Tss9GcAyVEuUqk');
52     */
53    static function getDidDataAccountId($base58SubjectPk): string
54    {
55
56        $b58 = new Base58();
57        $seeds = array(self::DIDSOL_DEFAULT_SEED, $b58->decode($base58SubjectPk));
58        $pId = new PublicKey(self::DIDSOL_PROGRAM_ID);
59        $publicKey =  PublicKey::findProgramAddress($seeds, $pId);
60
61        return $publicKey[0]->toBase58();
62    }
63
64    /**
65     * deserializeDidData
66     *
67     * @param string $dataBase64 The base64 encoded data of the DID data account
68     * @return DidData The deserialized DID data object
69     * @example DidSolProgram::deserializeDidData('TVjvjfsd7fMA/gAAAA...');
70     */
71    static function deserializeDidData($dataBase64)
72    {
73
74        $base64String = base64_decode($dataBase64);
75        $uint8Array = array_values(unpack('C*', $base64String));
76        $didData = DidData::fromBuffer($uint8Array);
77
78        $keyData = $didData->keyData;
79
80        $binaryString = pack('C*', ...$keyData);
81
82        $b58 = new Base58();
83        $base58String = $b58->encode($binaryString);
84        $didData->keyData = $base58String;
85        return $didData;
86    }
87
88
89    public function parse(string $did) : array
90    {
91
92        $did = explode(":", $did);
93        if ($did[0] !== 'did' ||  count($did) < 3) {
94            throw new \Exception('Invalid DID format, use did:sol:[network:]base58SubjectPK');
95        }
96        if ($did[1] !== 'sol') {
97            throw new \Exception('Unsupported DID method, use did:sol:[network:]base58SubjectPK');
98        }
99        if (count($did) == 4) {
100            $network = $did[2];
101            $base58SubjectPK = $did[3];
102        }else if (count($did) == 3) {
103            $network = 'mainnet';
104            $base58SubjectPK = $did[2];
105        }
106
107        $rpcEndpoint = $this->getRpcEndpointFromShortcut($network);
108
109        return [
110            'network' => $network,
111            'base58SubjectPK' => $base58SubjectPK,
112            'dataAccountId' => self::getDidDataAccountId($base58SubjectPK),
113            'rpcEndpoint' => $rpcEndpoint
114        ];
115    }
116
117    private function getRpcEndpointFromShortcut(string $network){
118        switch ($network) {
119            case 'mainnet':
120                $rpcEndpoint = SolanaRpcClient::MAINNET_ENDPOINT;
121                break;
122            case 'devnet':
123                $rpcEndpoint = SolanaRpcClient::DEVNET_ENDPOINT;
124                break;
125            case 'testnet':
126                $rpcEndpoint = SolanaRpcClient::TESTNET_ENDPOINT;
127                break;
128            default:
129                $rpcEndpoint = SolanaRpcClient::MAINNET_ENDPOINT;
130        }
131        return $rpcEndpoint;
132    }
133
134
135}