@@ -47,6 +47,7 @@ that can pay the gas fee for every voting transaction, allowing all citizens to
47
47
## Recommended reading
48
48
49
49
* [ The First Crypto Gas Station is Now on Kadena’s Blockchain] ( /blogchain/2020/the-first-crypto-gas-station-is-now-on-kadenas-blockchain-2020-08-06 )
50
+ * [ Introducing Kadena Account Protocols (KIP-0012)] ( /blogchain/2021/introducing-kadena-account-protocols-kip-0012-2021-09-27 )
50
51
51
52
## Get the code
52
53
@@ -214,12 +215,9 @@ The test will now fail with
214
215
` Error: found unimplemented member while resolving model constraints: create-gas-payer-guard ` .
215
216
Indeed, there is a function ` create-gas-payer-guard ` defined in the ` gas-payer-v1 ` interface
216
217
that still needs to be implemented. The documentation inside is a bit cryptic, but it suggests
217
- to require something like the ` GAS_PAYER ` capability without the parameters. You can use
218
- the ` GAS ` capability from the ` coin ` module here. After all, in Chainweaver's module explorer
219
- you can find that this capability is documented as ` Magic capability to protect gas buy and redeem ` .
220
- Sounds legit! Implement ` create-gas-payer-guard ` as follows, using the built-in functions
221
- ` create-user-guard ` and ` require-capability ` . You can use ` GAS ` directly if you load the
222
- ` coin ` module in the ` election-gas-station ` module.
218
+ to require something like the ` GAS_PAYER ` capability without the parameters. To achieve this,
219
+ you can leverage the built-in function ` create-capability-guard ` and pass the ` ALLOW_GAS `
220
+ capability into it. The function will return a guard that requires the respective capability.
223
221
224
222
``` pact
225
223
(namespace 'n_fd020525c953aa002f20fb81a920982b175cdf1a)
@@ -231,8 +229,6 @@ Sounds legit! Implement `create-gas-payer-guard` as follows, using the built-in
231
229
232
230
(implements gas-payer-v1)
233
231
234
- (use coin)
235
-
236
232
(defcap GAS_PAYER:bool
237
233
( user:string
238
234
limit:integer
@@ -244,11 +240,7 @@ Sounds legit! Implement `create-gas-payer-guard` as follows, using the built-in
244
240
(defcap ALLOW_GAS () true)
245
241
246
242
(defun create-gas-payer-guard:guard ()
247
- (create-user-guard (gas-payer-guard))
248
- )
249
-
250
- (defun gas-payer-guard ()
251
- (require-capability (GAS))
243
+ (create-capability-guard (ALLOW_GAS))
252
244
)
253
245
)
254
246
```
@@ -300,31 +292,54 @@ function change the line `.addSigner(accountKey(account))` into the following.
300
292
``` pact
301
293
.addSigner(accountKey(account), (withCapability) => [
302
294
withCapability(`${NAMESPACE}.election-gas-station.GAS_PAYER`, account, { int: 0 }, { decimal: '0.0' }),
303
- withCapability('coin.GAS'),
304
295
])
305
296
```
306
297
307
- This scopes the signature of the account that votes to two capabilities. The ` coin.GAS ` capability is used
308
- in the ` create-gas-payer-guard ` function of the ` election-gas-station ` module. The voter account name and
309
- zero (unlimited) limits for the amount of gas and the gas price are passed into the
310
- ` ${NAMESPACE}.election-gas-station.GAS_PAYER ` capability.
311
-
312
- Also, change the ` senderAccount ` in the transaction's metadata to ` 'election-gas-station' ` .
298
+ This scopes the signature of the account that votes to the ` GAS_PAYER ` capability. The voter account name and
299
+ zero (unlimited) limits for the amount of gas and the gas price are passed as arguments. Also, change the
300
+ ` senderAccount ` in the transaction's metadata to ` 'election-gas-station' ` , to indicate that the election
301
+ gas station account will pay the gas fee of the transaction instead of the voter account.
313
302
314
303
Return to the election website and try to vote again with the voter account. The transaction will still fail
315
304
with the error: ` Failure: Tx Failed: Insufficient funds ` . Apparently, the gas station does not work as it is
316
305
supposed to, yet. The reason is that the gas station module attempts to pay for gas using the ` senderAccount ` ,
317
306
but this account does not exist. It has to be created first. It also needs to have a positive KDA balance.
318
307
Otherwise, the transaction will still fail due to insufficient funds in the gas station account.
319
308
320
- ## Create and fund the gas station account
309
+ ## Create the gas station account
321
310
322
- The ` coin ` module is already imported inside the ` election-gas-station ` module. You can use it to create the
323
- ` election-gas-station ` account in a function called ` init ` , as follows.
311
+ Actually, ` election-gas-station ` is not the most ideal name for the gas station account. As explained in the
312
+ recommended reading, it is more secure to use a principal account name. Whereas your admin and voter accounts
313
+ are guarded by a keyset, the gas station account will be guarded by the ` ALLOW_GAS ` capability. The gas station
314
+ account is thus an example of a capability guarded account. The built-in Pact function ` create-pincipal ` can
315
+ automatically create an account name based on a capability guard for you if you pass the capability guard as
316
+ the first and only argument into it. The resulting account name will be prefixed with the ` c: ` of ` capability ` .
317
+ Define the gas station account name as a constant at the bottom of the ` election-gas-station ` module in the
318
+ ` ./pact/election-gas-station.pact ` file.
324
319
325
320
``` pact
326
- (defconst GAS_STATION_ACCOUNT "election-gas-station")
321
+ (defconst GAS_STATION_ACCOUNT (create-principal (create-gas-payer-guard)))
322
+ ```
323
+
324
+ Update the ` ./pact/election-gas-station.repl ` file as follows to print out the capability guarded gas station
325
+ account name when you run the file.
327
326
327
+ ``` pact
328
+ (load "setup.repl")
329
+
330
+ (begin-tx "Load election gas station module")
331
+ (load "root/gas-payer-v1.pact")
332
+ (load "election-gas-station.pact")
333
+ [GAS_STATION_ACCOUNT]
334
+ (commit-tx)
335
+ ```
336
+
337
+ In the ` ./pact/election-gas-station.pact ` file, you can use the ` create-account ` function of the
338
+ ` coin ` module to create the gas station account in a function called ` init ` in the ` election-gas-station `
339
+ module, as follows. The first argument of the function is the account name you just defined and the second
340
+ argument is the guard for the account.
341
+
342
+ ``` pact
328
343
(defun init ()
329
344
(coin.create-account GAS_STATION_ACCOUNT (create-gas-payer-guard))
330
345
)
@@ -357,51 +372,68 @@ in the `./snippets` folder of your project. Replace `k:account` with your admin
357
372
npm run deploy-gas-station:devnet -- k:account upgrade init
358
373
```
359
374
360
- Verify that the ` election-gas-station ` account now exists with a 0 KDA balance on Devnet by running the
361
- following script.
375
+ Verify that the gas station account now exists with a 0 KDA balance on Devnet by running the
376
+ following script. Replace ` c:account ` with the actual gas station account name that you printed by running
377
+ ` ./pact/election-gas-station.repl ` .
362
378
363
379
``` bash
364
- npm run coin-details:devnet -- election-gas-station
380
+ npm run coin-details:devnet -- c:account
365
381
```
366
382
367
383
If everything went well, you should see output similar to this.
368
384
369
385
``` bash
370
386
{
371
387
guard: {
372
- args: [],
373
- fun: ' n_fd020525c953aa002f20fb81a920982b175cdf1a.election-gas-station.gas-payer-guard'
388
+ cgPactId: null,
389
+ cgArgs: [],
390
+ cgName: ' n_fd020525c953aa002f20fb81a920982b175cdf1a.election-gas-station.ALLOW_GAS'
374
391
},
375
392
balance: 0,
376
- account: ' election-gas-station '
393
+ account: ' c:Jjn2uym_xGD32ojhWdPjB5mgIbDwgXRRvkWmFl5n4gg '
377
394
}
378
395
```
379
396
397
+ The account details show the capability guard that guards the gas station account and was used to generate
398
+ the ` c: ` account name. Notice how the ` ALLOW_GAS ` capability is prefixed with the module name as well as your
399
+ principal namespace. Since the principal namespace is based on your admin keyset, and the principal account
400
+ of the gas station is based on a capability including that principal namespace, it can be concluded that the
401
+ gas station account name you created is unique to your admin account. This makes it impossible for someone else
402
+ with a different keyset to squat your gas station account on another chain. That is how principal accounts in
403
+ principal namespaces provide better security than vanity account names in the ` free ` namespace.
404
+
405
+ ## Fund the gas station account
406
+
380
407
Execute the ` ./transfer.ts ` snippet by running the following command to transfer 1 KDA from your admin
381
- account to the gas station account. Replace ` k:account ` with your admin account. The transaction
408
+ account to the gas station account. Replace ` k:account ` with your admin account and replace ` c:account `
409
+ with the actual account name of your gas station. The transaction
382
410
inside this file is similar to ` ./transfer-create.ts ` , except that it does not use the special
383
411
` sender00 ` account, but your own election admin account to transfer KDA from. Therefore, the transaction
384
412
needs to be signed with Chainweaver instead of a private key. Also, the ` transfer ` function of the
385
413
` coin ` module is used. This function requires that the receiving account already exists on the
386
414
blockchain and will not create the account if it does not exist like ` transfer-create ` would.
387
415
388
416
``` bash
389
- npm run transfer:devnet -- k:account election-gas-station 1
417
+ npm run transfer:devnet -- k:account c:account 1
390
418
```
391
419
392
- Verify that the ` election-gas-station ` account now has a 1 KDA balance on Devnet by running the
393
- following script again.
420
+ Verify that the election gasstation account now has a 1 KDA balance on Devnet by running the
421
+ following script again. Replace ` c:account ` with the actual account name of your gas station.
394
422
395
423
``` bash
396
- npm run coin-details:devnet -- election-gas-station
424
+ npm run coin-details:devnet -- c:account
397
425
```
398
426
399
427
Now, everything should be set to allow voters to vote for free, because the ` election-gas-station `
400
428
account can pay the gas fee charged for the voting transaction.
401
429
402
430
## Vote again
403
431
404
- Return to the election website, set the account to your voter account and vote for one of the
432
+ Open the file ` frontend/src/repositories/vote/DevnetVoteRepository.ts ` and in the ` vote `
433
+ function change the value of ` senderAccount ` from ` election-gas-station ` to the ` c:account ` of the gas
434
+ station that you created.
435
+
436
+ Vist the election website in your browser, set the account to your voter account and vote for one of the
405
437
candidates in the list. Unfortunately, the transaction still fails but this time with a
406
438
different error: ` Keyset failure ` . This error occurs because the signature is not scoped to
407
439
the ` ACCOUNT-OWNER ` capability used in ` ./pact/election.repl ` . When you created this capability
@@ -430,10 +462,10 @@ The `caps` field in the signature passed to `env-sigs` is an empty array. As a c
430
462
signature of the transaction is not scoped to any capability and the signer automatically
431
463
approves all capabilities required for the function execution. In the ` vote ` function of
432
464
` frontend/src/repositories/vote/DevnetVoteRepository.ts ` you scoped the signature of the
433
- transaction to two gas related capabilities , but not to the ` ACCOUNT-OWNER ` capability. When
465
+ transaction to the ` GAS_PAYER ` capability , but not to the ` ACCOUNT-OWNER ` capability. When
434
466
you sign for some capabilities but not all capabilities required for execution of a transaction,
435
467
the execution will fail at the point where a capability is required that you did not sign for.
436
- Therefore, you need to add a third capability to the array passed to ` addSigners ` in
468
+ Therefore, you need to add a second capability to the array passed to ` addSigners ` in
437
469
the ` vote ` function in ` frontend/src/repositories/vote/DevnetVoteRepository.ts ` .
438
470
439
471
``` typescript
0 commit comments