Create an account using the Java SDK

I have some confusion when using java sdk to register an account.
The following is the test class of the SDK. I can’t understand what values ​​​​the data and signer need to pass in, public keys or other values, and where these values ​​​​are obtained

 @Test
    public void testCreateTransferWithRegisterData() {
        try {
            RegisterDataTransaction tx = TransactionFactory.newRegisterData()
                    .data(Data.from(new byte[]{1, 2, 3, 4, 5}))
                    .sender(AccountAddress.from("3JwD2Wm3nMbsowCwb1iGEpnt47UQgdrtnq2qT6opJc3z2AgCrc"))
                    .nonce(Nonce.from(78910))
                    .expiry(Expiry.from(123456))
                    .signer(TransactionSigner.from(
                            SignerEntry.from(Index.from(0), Index.from(0),
                                    ED25519SecretKey.from("7100071c835a0a35e86dccba7ee9d10b89e36d1e596771cdc8ee36a17f7abbf2")),
                            SignerEntry.from(Index.from(0), Index.from(1),
                                    ED25519SecretKey.from("cd20ea0127cddf77cf2c20a18ec4516a99528a72e642ac7deb92131a9d108ae9"))
                    ))
                    .build();
            val transferWithRegisterData = tx.getPayload();

            assertEquals(UInt64.from(568), transferWithRegisterData.header.getMaxEnergyCost());
            assertEquals(8, transferWithRegisterData.getBytes().length);
            val blockItem = transferWithRegisterData.toBlockItem();
            assertEquals(Hash.from("0ad44be061cbdfc22fdcf14e2cd48d7c34d543ea60cb9cf5298cb40d89c25d83"), blockItem.getHash());
            assertEquals(blockItem.getHash(), BlockItem.fromVersionedBytes(ByteBuffer.wrap(blockItem.getVersionedBytes())).getHash());
            assertArrayEquals(TestUtils.EXPECTED_BLOCK_ITEM_TRANSFER_WITH_REGISTER_DATA_VERSIONED_BYTES, TestUtils.signedByteArrayToUnsigned(blockItem.getVersionedBytes()));
        } catch (TransactionCreationException e) {
            fail("Unexpected error: " + e.getMessage());
        }
    }

Hi sandy,

This test case does not have to do with creating an account - it’s a “register data” transaction.

The RegisterData transaction that is used in this example is a transaction that records some (arbitrary) data on the chain. This is typically used for notarization: by recording a hash of some data, you can establish that the data was created by the time of the transaction. In this case, the data (here the byte array {1,2,3,4,5}) can be any array of bytes up to 256 bytes in length.

The signer needs to be initialized with the secret key(s) for the account that is sending the transaction. In this example, there are two keys, but typically accounts only have one key (so you would just provide one key with indexes 0 and 0). The Java SDK does not really provide much support for key management. You can export private keys from the concordium wallets that you can then use for signing.

To actually create an account is a bit complicated. First, you need an identity object. To create an identity object, you need to create an identity issuance request and submit it to an identity provider. The user then has to complete the identity issuance flow (specific to the identity provider), which results in the creation of an identity object.

You can then use the identity object to create a CredentialDeploymentTransaction, which can be submitted to the chain to create an account. (See here for an example.) I recommend looking over the concordium-android-wallet-example in depth to see how the identity issuance and account creation process works.

Hope this helps
– Thomas

OK, thank you for your answer. I have other questions. What data does this identity object contain? When I create an address, I call a separate service of our exchange and calculate the address offline through the public key. In this process, I can only get the address and public key information, but I can’t get the private key. Can I complete the registration of this address in this way? The following is my code for generating an address offline.

  public String generate(String publicKey) throws DecoderException {

        byte[] publicBytes = Hex.decodeHex(publicKey);

        byte[] input = new byte[33];
        input[0] = (byte)1;
        System.arraycopy(publicBytes, 0, input, 1, 32);
        byte[] hash1 = Hash.sha256(input);
        byte[] hash2 = Hash.sha256(hash1);
        byte[] result = new byte[33 + 4];
        System.arraycopy(input, 0, result, 0 ,33);
        System.arraycopy(hash2, 0, result, 33 ,4);

        return Base58.encode(result);

    }

The identity object consists of:

  • A “pre-identity object”, which is the information sent by the account holder to the identity provider. This includes various cryptographic data that allows identity revocation authorities to discover the identity associated with accounts created using the identity object.
  • A list of attributes associated with the identity (e.g. name, nationality, date of birth) that are certified by the identity provider.
  • The identity provider’s signature on the identity.

I’m a bit unclear about what you mean by “create an address”. When an account is created on Concordium, its address is calculated by hashing (SHA256) the registration id (technically, this is an element of the G1 group of the BLS curve) of the credential used to create the account. This gives the canonical address of the account, but the account can also be referred to by any address that only differs from the canonical address in the last 3 bytes (these are “aliases” for the account).

I believe that your generate function computes the Base58-checked representation of the account address from the hexadecimal encoding of its address (i.e. the SHA256 hash of the credential registration id).