Skip to content

Commit 2de76bd

Browse files
authored
Merge pull request #978 from Concordium/android-examples
Android examples
2 parents 1178e85 + 4b1e399 commit 2de76bd

File tree

5 files changed

+513
-58
lines changed

5 files changed

+513
-58
lines changed

source/mainnet/net/guides/wallet-sdk/wallet-sdk-account-transaction.rst

Lines changed: 79 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@ Submit a transaction to a Concordium node
77
The following sections document the requirements for creating an account transaction, signing it, and
88
sending it to a Concordium node.
99

10-
* `Construct an account transaction`_
11-
* `Sign an account transaction`_
10+
* `Construct and sign an account transaction`_
1211
* `Send an account transaction to a Concordium node`_
1312

14-
++++++++++++++++++++++++++++++++
15-
Construct an account transaction
16-
++++++++++++++++++++++++++++++++
13+
+++++++++++++++++++++++++++++++++++++++++
14+
Construct and sign an account transaction
15+
+++++++++++++++++++++++++++++++++++++++++
1716

18-
This example constructs a simple transfer, which is an account transaction that moves an amount of CCD from one account to another. For other transaction types, the steps are similar, but the exact fields that must be provided for the payload will be different.
17+
This example constructs and signs a simple transfer, which is an account transaction that moves an amount of CCD from one account to another. For other transaction types, the steps are similar, but the exact fields that must be provided for the payload will be different. Note that Concordium as a whole supports multi-signature transactions, but for the purpose of this example it will demonstrate how to sign for an account with a single credential that has a single key.
18+
19+
Note that when the transaction has been signed anyone with the signature and the transaction will be able to send it to a Concordium node. Therefore it is very important that a wallet requests user approval before utilizing their signing keys.
1920

2021
.. tabs::
2122

@@ -29,6 +30,7 @@ This example constructs a simple transfer, which is an account transaction that
2930
AccountAddress,
3031
AccountTransaction,
3132
AccountTransactionHeader,
33+
buildBasicAccountSigner,
3234
CcdAmount,
3335
ConcordiumGRPCWebClient,
3436
ConcordiumHdWallet,
@@ -73,44 +75,6 @@ This example constructs a simple transfer, which is an account transaction that
7375
header,
7476
};
7577
76-
.. tab::
77-
78-
Kotlin (Android)
79-
80-
TODO Write the Kotlin example.
81-
82-
.. tab::
83-
84-
Swift (iOS)
85-
86-
The Swift SDK for iOS is still in development.
87-
88-
+++++++++++++++++++++++++++
89-
Sign an account transaction
90-
+++++++++++++++++++++++++++
91-
92-
Having constructed an account transaction, the next step is to sign it. It is important that the key used to sign an account transaction matches the sender address provided in the account transaction header. Note that Concordium as a whole supports multi-signature transactions, but for the purpose of this example it will demonstrate how to do it for an account with a single credential that has a single key.
93-
94-
Note that when the transaction has been signed anyone with the signature and the transaction will be able to send it to a Concordium node. Therefore it is very important that a wallet requests user approval before utilizing their signing keys.
95-
96-
.. tabs::
97-
98-
.. tab::
99-
100-
TypeScript (Web)
101-
102-
.. code-block:: javascript
103-
104-
import {
105-
buildBasicAccountSigner,
106-
ConcordiumHdWallet,
107-
signTransaction,
108-
} from '@concordium/web-sdk';
109-
110-
const seedPhrase = 'fence tongue sell large master side flock bronze ice accident what humble bring heart swear record valley party jar caution horn cushion endorse position';
111-
const network = 'Testnet'; // Or Mainnet, if working on mainnet.
112-
const wallet = ConcordiumHdWallet.fromSeedPhrase(seedPhrase, network);
113-
11478
const signingKey = wallet.getAccountSigningKey(identityProviderIndex, identityIndex, credNumber);
11579
const signer = buildBasicAccountSigner(signingKey.toString('hex'));
11680
@@ -120,7 +84,62 @@ Note that when the transaction has been signed anyone with the signature and the
12084

12185
Kotlin (Android)
12286

123-
TODO Write the Kotlin example.
87+
.. code-block:: Kotlin
88+
89+
import cash.z.ecc.android.bip39.Mnemonics
90+
import cash.z.ecc.android.bip39.toSeed
91+
import com.concordium.sdk.ClientV2
92+
import com.concordium.sdk.Connection
93+
import com.concordium.sdk.TLSConfig
94+
import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet
95+
import com.concordium.sdk.crypto.wallet.Network
96+
import com.concordium.sdk.requests.BlockQuery
97+
import com.concordium.sdk.transactions.CCDAmount
98+
import com.concordium.sdk.transactions.CredentialRegistrationId
99+
import com.concordium.sdk.transactions.Expiry
100+
import com.concordium.sdk.transactions.Index
101+
import com.concordium.sdk.transactions.SignerEntry
102+
import com.concordium.sdk.transactions.TransactionFactory
103+
import com.concordium.sdk.transactions.TransactionSigner
104+
import com.concordium.sdk.types.AccountAddress
105+
106+
fun createTransferTransaction(): TransferTransaction {
107+
val seedPhrase = "fence tongue sell large master side flock bronze ice accident what humble bring heart swear record valley party jar caution horn cushion endorse position"
108+
109+
@OptIn(ExperimentalStdlibApi::class)
110+
val seedAsHex = Mnemonics.MnemonicCode(seedPhrase!!.toCharArray()).toSeed().toHexString()
111+
val network = Network.TESTNET // Or Network.MAINNET, if working on mainnet.
112+
val wallet = ConcordiumHdWallet.fromHex(seedAsHex, Network.TESTNET)
113+
114+
val connection = Connection.newBuilder()
115+
.host(nodeAddress)
116+
.port(nodePort)
117+
.useTLS(TLSConfig.auto())
118+
.build()
119+
val client = ClientV2.from(connection)
120+
121+
val cryptographicParameters = client.getCryptographicParameters(BlockQuery.BEST)
122+
123+
val credId = wallet.getCredentialId(identityProviderIndex, identityIndex, credNumber, cryptographicParameters.onChainCommitmentKey.toHex())
124+
val sender = AccountAddress.from(CredentialRegistrationId.from(credId))
125+
126+
val toAddress = AccountAddress.from("4QkqdUnrjShrUrHpE96odLM6J77nWzEryifzqNnwNk4FYNge8a")
127+
val amount = CCDAmount.from(5000000)
128+
129+
val nonce = client.getNextAccountSequenceNumber(sender)
130+
val expiry = Expiry.createNew().addMinutes(5)
131+
132+
val signingKey = wallet.getAccountSigningKey(identityProviderIndex, identityIndex, credNumber)
133+
134+
val signer = TransactionSigner.from(
135+
SignerEntry.from(
136+
Index.from(0), Index.from(0),
137+
signingKey
138+
)
139+
)
140+
return TransactionFactory.newTransfer().sender(sender).receiver(toAddress).amount(amount)
141+
.nonce(nonce).expiry(expiry).signer(signer).build()
142+
}
124143
125144
.. tab::
126145

@@ -153,7 +172,21 @@ Finally, when the transaction has been constructed and signed, it is ready to be
153172

154173
Kotlin (Android)
155174

156-
TODO Write the Kotlin example.
175+
.. code-block:: Kotlin
176+
177+
import com.concordium.sdk.ClientV2
178+
import com.concordium.sdk.Connection
179+
import com.concordium.sdk.TLSConfig
180+
181+
fun main() {
182+
val connection = Connection.newBuilder()
183+
.host(nodeAddress)
184+
.port(nodePort)
185+
.useTLS(TLSConfig.auto())
186+
.build()
187+
val client = ClientV2.from(connection)
188+
val transactionHash = client.sendTransaction(transaction)
189+
}
157190
158191
.. tab::
159192

source/mainnet/net/guides/wallet-sdk/wallet-sdk-credential-deployment.rst

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ The following example demonstrates how a credential deployment transaction is cr
3333
const identity: IdentityObjectV1 = ...;
3434
3535
// The identity provider that was used when creating the identity.
36-
const identityProvider: IdentityProvider = ...;
36+
const identity: IdentityProvider = ...;
37+
const identityProviderIndex = identityProvider.ipInfo.ipIdentity;
3738
3839
// Set up the wallet.
3940
const seedPhrase = 'fence tongue sell large master side flock bronze ice accident what humble bring heart swear record valley party jar caution horn cushion endorse position';
@@ -89,7 +90,93 @@ The following example demonstrates how a credential deployment transaction is cr
8990

9091
Kotlin (Android)
9192

92-
TODO Write the Kotlin example.
93+
.. code-block:: Kotlin
94+
95+
import cash.z.ecc.android.bip39.Mnemonics
96+
import cash.z.ecc.android.bip39.toSeed
97+
import com.concordium.example.wallet.services.ConcordiumClientService
98+
import com.concordium.example.wallet.services.ConcordiumWalletProxyService
99+
import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet
100+
import com.concordium.sdk.crypto.wallet.Credential
101+
import com.concordium.sdk.crypto.wallet.Network
102+
import com.concordium.sdk.crypto.wallet.UnsignedCredentialInput
103+
import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentDetails
104+
import com.concordium.sdk.crypto.wallet.credential.UnsignedCredentialDeploymentInfoWithRandomness
105+
import com.concordium.sdk.requests.BlockQuery
106+
import com.concordium.sdk.responses.accountinfo.credential.AttributeType
107+
import com.concordium.sdk.transactions.CredentialPublicKeys
108+
import com.concordium.sdk.transactions.Expiry
109+
import com.concordium.sdk.transactions.Index
110+
import java.util.Collections
111+
import java.util.EnumMap
112+
113+
fun createCredentialDeploymentTransaction(identityIndex: Int, credentialCounter: Int): CredentialDeploymentDetails {
114+
// The identity object created previously. See the identity creation section.
115+
val identity: IdentityObject = ...
116+
117+
// The index of the identity provider that was used for creating the identity.
118+
val identityProviderIndex = ...
119+
120+
val connection = Connection.newBuilder()
121+
.host(nodeAddress)
122+
.port(nodePort)
123+
.useTLS(TLSConfig.auto())
124+
.build()
125+
val client = ClientV2.from(connection)
126+
127+
val anonymityRevokers = Iterable { client.getAnonymityRevokers(BlockQuery.BEST) }.associateBy { it.arIdentity.toString() }
128+
val providers = client.getIdentityProviders(BlockQuery.BEST)
129+
val provider = Iterable { providers }.find { it.ipIdentity.value == identityProviderIndex }!!
130+
val global = client.getCryptographicParameters(BlockQuery.BEST)
131+
132+
val seedPhrase = "fence tongue sell large master side flock bronze ice accident what humble bring heart swear record valley party jar caution horn cushion endorse position"
133+
@OptIn(ExperimentalStdlibApi::class)
134+
val seedAsHex = Mnemonics.MnemonicCode(seedPhrase.toCharArray()).toSeed().toHexString()
135+
val wallet = ConcordiumHdWallet.fromHex(seedAsHex, Network.TESTNET)
136+
137+
val attributeRandomness: MutableMap<AttributeType, String> = EnumMap(AttributeType::class.java)
138+
for (attrType in identity.attributeList.chosenAttributes.keys) {
139+
attributeRandomness[attrType] = wallet.getAttributeCommitmentRandomness(
140+
identityProviderIndex,
141+
identityIndex,
142+
credentialCounter,
143+
attrType.ordinal
144+
)
145+
}
146+
147+
val blindingRandomness = wallet.getSignatureBlindingRandomness(identityProviderIndex, identityIndex)
148+
val idCredSec = wallet.getIdCredSec(identityProviderIndex, identityIndex)
149+
val prfKey = wallet.getPrfKey(identityProviderIndex, identityIndex)
150+
151+
val publicKeys = CredentialPublicKeys.from(
152+
Collections.singletonMap(
153+
Index.from(0),
154+
wallet.getAccountPublicKey(
155+
identityProviderIndex,
156+
identityIndex,
157+
credentialCounter
158+
)
159+
), 1
160+
)
161+
162+
val input: UnsignedCredentialInput = UnsignedCredentialInput.builder()
163+
.ipInfo(provider)
164+
.globalContext(global)
165+
.arsInfos(anonymityRevokers)
166+
.idObject(identity)
167+
.credNumber(credentialCounter)
168+
.attributeRandomness(attributeRandomness)
169+
.blindingRandomness(blindingRandomness)
170+
.credentialPublicKeys(publicKeys)
171+
.idCredSec(idCredSec)
172+
.prfKey(prfKey)
173+
.revealedAttributes(emptyList())
174+
.build()
175+
176+
val expiry = Expiry.createNew().addMinutes(5)
177+
178+
return CredentialDeploymentDetails(Credential.createUnsignedCredential(input).unsignedCdi, expiry)
179+
}
93180
94181
.. tab::
95182

@@ -140,7 +227,39 @@ is the signing key that corresponds to the public key used when creating the tra
140227

141228
Kotlin (Android)
142229

143-
TODO Write the Kotlin example.
230+
.. code-block:: Kotlin
231+
232+
import cash.z.ecc.android.bip39.Mnemonics
233+
import cash.z.ecc.android.bip39.toSeed
234+
import com.concordium.sdk.crypto.wallet.ConcordiumHdWallet
235+
import com.concordium.sdk.crypto.wallet.Credential
236+
import com.concordium.sdk.crypto.wallet.Network
237+
import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentDetails
238+
239+
fun signCredentialDeployment(credentialDeployment: CredentialDeploymentDetails): ByteArray {
240+
val seedPhrase = "fence tongue sell large master side flock bronze ice accident what humble bring heart swear record valley party jar caution horn cushion endorse position"
241+
@OptIn(ExperimentalStdlibApi::class)
242+
val seedAsHex = Mnemonics.MnemonicCode(seedPhrase.toCharArray()).toSeed().toHexString()
243+
val wallet = ConcordiumHdWallet.fromHex(seedAsHex, Network.TESTNET)
244+
245+
// The credentialCounter and the identityIndex must identical to what was used when deriving
246+
// the keys to create the credential deployment transaction.
247+
val credentialCounter = 0
248+
val identityIndex = 0
249+
250+
// The indentityProvider index must be indentical to the index of the identity provider
251+
// that was used to create the identity that the credential is for.
252+
val identityProviderIndex = 0
253+
254+
val credentialDeploymentSignDigest = Credential.getCredentialDeploymentSignDigest(credentialDeployment)
255+
val signingKey = wallet.getAccountSigningKey(
256+
identityProviderIndex,
257+
identityIndex,
258+
credentialCounter
259+
)
260+
261+
return signingKey.sign(credentialDeploymentSignDigest)
262+
}
144263
145264
.. tab::
146265

@@ -186,7 +305,38 @@ If successful, the credential will be deployed, and it is now possible to start
186305

187306
Kotlin (Android)
188307

189-
TODO Write the Kotlin example.
308+
.. code-block:: Kotlin
309+
310+
import com.concordium.sdk.crypto.wallet.Credential
311+
import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentDetails
312+
import com.concordium.sdk.crypto.wallet.credential.CredentialDeploymentSerializationContext
313+
import com.concordium.sdk.transactions.CredentialDeploymentTransaction
314+
import com.concordium.sdk.transactions.Index
315+
import org.apache.commons.codec.binary.Hex
316+
317+
fun sendCredentialDeploymentTransaction(): Hash {
318+
// The credential deployment details created in the first section.
319+
val credentialDeploymentDetails: CredentialDeploymentDetails = ...
320+
321+
// The signature on the credential deployment transaction from the previous section
322+
val signature: String = ...
323+
324+
val context = CredentialDeploymentSerializationContext(
325+
credentialDeployment.unsignedCdi,
326+
mapOf(Pair(Index.from(0), Hex.encodeHexString(signature)))
327+
)
328+
val credentialPayload = Credential.serializeCredentialDeploymentPayload(context)
329+
val credentialDeploymentTransaction = CredentialDeploymentTransaction.from(credentialDeploymentDetails.expiry, credentialPayload)
330+
331+
val connection = Connection.newBuilder()
332+
.host(nodeAddress)
333+
.port(nodePort)
334+
.useTLS(TLSConfig.auto())
335+
.build()
336+
val client = ClientV2.from(connection)
337+
338+
return client.sendCredentialDeploymentTransaction(credentialDeploymentTransaction)
339+
}
190340
191341
.. tab::
192342

0 commit comments

Comments
 (0)