Issue with GetAccountInfo in PHP gRPC API : Error in GetAccountInfo missing field

Description: I’m working with the new Concordium gRPC API https://github.com/Concordium/concordium-grpc-api to compile it for PHP and am trying to use the GetAccountInfo function with AccountInfoRequest as a parameter.

However, I’m encountering an issue where a “missing field” error is returned when using AccountInfoRequest . After investigating, it seems like the AccountInfoRequest type may be missing some fields, or the AccountAddress type that where called to create the AccountInfoRequest.

From the .proto files service.proto and the types.proto we have created the PHP gRPC files to interact with the gRPC

This is the example of our code:

// Example publicAddress
string $publicAddress = "4rvQePs6ZKFiW8rwY5nP18Uj2DroWiw9VPKTsTwfwmsjcFCJLy";
// Use PBEmpty as the request type for GetConsensusInfo
$client = new QueriesClient($grpcHostname, [
			'credentials' => ChannelCredentials::createInsecure(),
		]);
list($consensusInfoResponse, $status) = $client->GetConsensusInfo(new PBEmpty())->wait();
if ($status->code !== \Grpc\STATUS_OK) {
	throw new Exception("Error in GetConsensusInfo: " . $status->details);
}
// Retrieve the last finalized block from the consensus info
$lastFinalizedBlock = $consensusInfoResponse->getLastFinalizedBlock();

// Create BlockHash and BlockHashInput using the last finalized block
$blockHashInput = new BlockHashInput();
$blockHashInput->setGiven($lastFinalizedBlock);

// Create BlockHashInput using the last finalized block which is a BlockHash type
$blockHashInput = new BlockHashInput();
$blockHashInput->setGiven($lastFinalizedBlock);

// Create the AccountAddress instance using the publicAddress
$accountAddress = new AccountAddress();
$accountAddress->setValue($publicAddress);

// Create AccountIdentifierInput using the accountAddress
$accountIdentifier = new AccountIdentifierInput();
$accountIdentifier->setAddress($accountAddress);

// Create the AccountInfoRequest using the blockHashInput and the accountIdentifier
$accountInfoRequest = new AccountInfoRequest();
$accountInfoRequest->setAccountIdentifier($accountIdentifier);
$accountInfoRequest->setBlockHash($blockHashInput);

// Get the Account  info information
list($accountInfoResponse, $status) = $client->GetAccountInfo($accountInfoRequest)->wait();
if ($status->code !== \Grpc\STATUS_OK) {
	throw new Exception("Error in GetAccountInfo: " . $status->details);
}

At this point the GetAccountInfo returs an error Error in GetAccountInfo missing field
We have also tested with using GetNextAccountSequenceNumber which uses the AccountAddress directly as a parameter and that also is returning the same error.

string $publicAddress = "4rvQePs6ZKFiW8rwY5nP18Uj2DroWiw9VPKTsTwfwmsjcFCJLy";
// Create the AccountAddress instance
$accountAddress = new AccountAddress();
$accountAddress->setValue($publicAddress);

list($nextAccountSequenceNumberResponse, $status) = $client->GetNextAccountSequenceNumber($accountAddress)->wait();
if ($status->code !== \Grpc\STATUS_OK) {
	throw new Exception("Error in GetNextAccountSequenceNumber: " . $status->details);
}

We have compared the JS library code for the AccountAddress which is using 2 parameters
https://github.com/Concordium/concordium-node-sdk-js/blob/main/packages/sdk/src/types/AccountAddress.ts#L28
but in when checking the types.proto file the AccountAddress only has one parameter
https://github.com/Concordium/concordium-grpc-api/blob/main/v2/concordium/types.proto#L341

It appears that some fields are not present or are not correctly mapped in the PHP gRPC implementation. Could this be a missing or incorrectly defined field in the AccountAddress type for the gRPC API?

Any insights or clarification on whether the AccountAddress type in the gRPC API is intended to differ from the Node SDK would be helpful.

Thank you!

Hi there

From the snippets you have provided, it seems that you are providing the base58check encoding of the account address, but the gRPC type AccountAddress expects the address encoded as the underlying 32 bytes.

You can see how the web SDK decode the base58check version of the address here.

If you want to take inspiration from the web SDK, look for the toProto and fromProto functions, since these handle the conversion between gRPC types and the JS native types.

The GRPC API requires the AccountAddress to be specified in raw bytes, rather than the base58check representation, and I think this is the cause of your problem here.

The base58check representation is derived from the raw byte representation as follows:

  • A version byte (0x01) is prepended.
  • The string is hashed twice using SHA-256 and the first four bytes of this hash are appended to the string.
  • The result is encoded in Base58.

I think something like the following should allow you to convert the address into the form required by the GRPC API.

// Base58 character set
$BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';

// Base58 decoding function
function base58_decode($base58) {
    global $BASE58_ALPHABET;

    $length = strlen($base58);
    $num = gmp_init(0);

    for ($i = 0; $i < $length; $i++) {
        $index = strpos($BASE58_ALPHABET, $base58[$i]);
        if ($index === false) {
            return false; // Invalid character for Base58
        }
        $num = gmp_add(gmp_mul($num, 58), $index);
    }

    // Convert GMP number to binary string
    $hex = gmp_strval($num, 16);

    // Ensure even length of the hex string
    if (strlen($hex) % 2 != 0) {
        $hex = '0' . $hex;
    }

    // Decode hex to binary
    return hex2bin($hex);
}

// Function to decode Base58Check AccountAddress
function decode_account_address($base58check) {
    // Step 1: Decode Base58
    $decoded = base58_decode($base58check);
    if ($decoded === false) {
        throw new Exception("Invalid Base58 encoding.");
    }

    // Step 2: Verify checksum (last 4 bytes)
    $payload = substr($decoded, 0, -4);
    $checksum = substr($decoded, -4);

    // Calculate checksum of the payload
    $calculatedChecksum = substr(hash('sha256', hash('sha256', $payload, true), true), 0, 4);

    if ($checksum !== $calculatedChecksum) {
        throw new Exception("Invalid checksum.");
    }

    // Step 3: Check the first byte (the version).
    $firstByte = ord($payload[0]);
    if ($firstByte !== 0x01) {
        throw new Exception("Version byte is not 0x01.");
    }

    // Step 4: Drop the version byte and return the remaining bytes
    return substr($payload, 1);
}
1 Like

@td202 @limemloh thank you for assistance and advice… i have tested now and i am getting the data from the gRPC when i did the base58 decode…
any ideas why the PHP gRPC does not have this functions in the AccountAddress class?
i was following the code from https://github.com/Concordium/concordium-net-sdk/blob/main/README.md

using Concordium.Grpc.V2;

BlockHashInput blockHashInput = new BlockHashInput() { LastFinal = new Empty() };

// Construct the input for the raw API.
AccountInfoRequest request = new AccountInfoRequest
{
  BlockHash = blockHashInput,
  /// Instantiate and convert Concordium.Sdk.Types.AccountAddress
  /// to an AccountIdentifierInput which is needed for the
  /// AccountInfoRequest.
  AccountIdentifier = Concordium.Sdk.Types.AccountAddress
    .From("4rvQePs6ZKFiW8rwY5nP18Uj2DroWiw9VPKTsTwfwmsjcFCJLy")
    .ToAccountIdentifierInput()
};

AccountInfo accountInfo = client.Raw.GetAccountInfo(request);

The SDKs (such as the Javascript and .net SDKs you mentioned above) provide a bunch of convenience wrappers around the gRPC API to make it more convenient to use - including converting between the Base58-checked and internal representation of account addresses. Unfortunately, we don’t have a PHP SDK, so you have to work directly with the gRPC API and provide the extra conversions yourself.

1 Like