At the time of writing, and prior to multi-sig support for the Radix wallet, validator owner management requires the signer to have soul possession of the Validator Owner Badge - an NFT that conveys the authorisation to update validator fees, change metadata and registration/deregistration.
I have provided a thread here (Validator Transaction Manifests) that details some of the common manifest instructions for managing a validator for reference.
This approach of having a single validator badge raises a specific problem -
How does a validator owner delegate the management of their node to a third party, such as a business partner, friend or indeed another node-runner?
The purpose of this post is to introduce an Access Management Component, built by Ahmed, owner of the RadixPlanet node which we have tested and incorporated into the Planet Node, allowing us to both manage the validator.
In simple terms, the Access Management Component requires the node-owner to deposit the Owner Badge into the component, and then mint any number of âAccess Keyâ badges which can be used as the authorisation for the component to perform the validator actions.
This allows multiple users to interact with the validator methods, but gives the node-owner ultimate control of the validator owner badge, as itâs withdrawal is restricted to the holder of the âAccess Management Component Ownerâ badge. This component owner badge can also recall, mint and burn âAccess Keyâ badges if permission needs to be revoked for any reason.
Note: The primary use case of this component is for managing validator owner badges, but it can be used for any case where a single NFT is required for performing actions (such as token minting authority for example).
Usage
Note: the Github repo containing the source code can be found here: Github Link
The blueprint package address for mainnet is as follows:
package_rdx1p4m04kkm8tw3fefwrf7zvgxjw8k0n9t30vawgq2kl90q3r77nf59w8
- Firstly, we must create a new component to hold our validator owner badge. Use the following manifest with the package address above to do so:
CALL_FUNCTION
Address("package_rdx1p4m04kkm8tw3fefwrf7zvgxjw8k0n9t30vawgq2kl90q3r77nf59w8")
"AccessManager"
"new"
Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey");
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"deposit_batch"
Expression("ENTIRE_WORKTOP");
This manifest creates a new Access Manager component, and we have specified the validator owner badge resource address, along with the account that is to be designated as owner. On submitting this tx, an âAccess Manager Ownerâ badge will be deposited to the ownerâs account.
Once the tx is confirmed, review the tx details in the dashboard and observe the âcreated entitiesâ in the details tab. Here you can find the address of the newly created component. In this example, the component address is:
component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860
- Once the component is created, the next step is to deposit the validator owner badge into the component. The manifest to do this is as follows:
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"withdraw_non_fungibles"
Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
Array<NonFungibleLocalId>(NonFungibleLocalId("[8361508c284bc2aa0795a1d28ae8f37146b701bd7390575697425eb2d08d]"));
TAKE_NON_FUNGIBLES_FROM_WORKTOP
Address("resource_rdx1nfxxxxxxxxxxvdrwnrxxxxxxxxx004365253834xxxxxxxxxvdrwnr")
Array<NonFungibleLocalId>(NonFungibleLocalId("[8361508c284bc2aa0795a1d28ae8f37146b701bd7390575697425eb2d08d]"))
Bucket("auth_badge_bucket");
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"create_proof_of_non_fungibles"
Address("resource_rdx1n2r24wvth7ul7jjqtnpm6s9a52lfq7a32lx6ez9chrjq4mgsp8cf6e")
Array<NonFungibleLocalId>(NonFungibleLocalId("{6f654d2d58611380-a3b3b32537ca549f-02b25f6411497e41-b1de3f49f2dadedb}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"deposit_auth_badge"
Bucket("auth_badge_bucket");
This manifest withdraws the validator owner badge from the account and places it into a bucket. It then creates a proof of the âAccess Manager Ownerâ badge created in step 1, before calling a method on the âAccess Managerâ component to deposit the validator owner badge.
Once the tx is confirmed, the Validator Owner badge will now be deposited into the component.
- In order to interact with the Validator Owner badge through the component, we need to create any number of âAccess Keyâ badges. These badges can be provided to anyone whom we wish to delegate the control of the validator to. To create an âAccess Keyâ badge, the following manifest is used:
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"create_proof_of_non_fungibles"
Address("resource_rdx1n2r24wvth7ul7jjqtnpm6s9a52lfq7a32lx6ez9chrjq4mgsp8cf6e")
Array<NonFungibleLocalId>(NonFungibleLocalId("{6f654d2d58611380-a3b3b32537ca549f-02b25f6411497e41-b1de3f49f2dadedb}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"create_access_key_badge";
CALL_METHOD
Address("account_rdx12yugeppnu2sul2qnry7nscpc9aglm922ygzkvvplp37p3rwvr4z7xz")
"try_deposit_batch_or_abort"
Expression("ENTIRE_WORKTOP")
None;
The manifest above provides a proof of the âAccess Manager Ownerâ badge, in order to mint a new access key badge by the component. This is then deposited into the delegates account, in this example, account_rdx12yugeppnu2sul2qnry7nscpc9aglm922ygzkvvplp37p3rwvr4z7xz
- Once the validator owner badge is in the component and your delegated validator managers have an âAccess Keyâ badge, they can then conduct any actions on the validator using the proof of this key badge. In order to do this, the following manifest should be used in conjunction with any of the standard validator manifests:
CALL_METHOD
Address("account_rdx12yugeppnu2sul2qnry7nscpc9aglm922ygzkvvplp37p3rwvr4z7xz")
"create_proof_of_non_fungibles"
Address("resource_rdx1n2ufdpyuundgtu68nvfdygyj74n7sqs32lsu76smvnt8j3v9976tmw")
Array<NonFungibleLocalId>(NonFungibleLocalId("{33a834fe1e62c7ce-d5ed40abff224b74-6dd84f924959af76-d78010d7932dd06b}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"create_auth_badge_proof";
The above manifest provides a proof of the âAccess Keyâ badge and presents it to the component. This will then allow the validator actions to be performed by the component itself. The above manifest can be used in conjunction with any of the validator manifest actions detailed in the Validator Transaction Manifest thread. For example, if we want to update the validatorâs fee, we would combine the manifest above with the âupdate_feeâ method to give:
CALL_METHOD
Address("account_rdx12yugeppnu2sul2qnry7nscpc9aglm922ygzkvvplp37p3rwvr4z7xz")
"create_proof_of_non_fungibles"
Address("resource_rdx1n2ufdpyuundgtu68nvfdygyj74n7sqs32lsu76smvnt8j3v9976tmw")
Array<NonFungibleLocalId>(NonFungibleLocalId("{33a834fe1e62c7ce-d5ed40abff224b74-6dd84f924959af76-d78010d7932dd06b}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"create_auth_badge_proof";
CALL_METHOD
Address("validator_rdx1sds4prpgf0p25pu458fg468nw9rtwqdawwg9w45hgf0t95yd3ncs09")
"update_fee"
Decimal("0.0199");
From the manifest above, we can see that instead of providing a proof of the validator owner badge (as is normally the case), we are providing a proof of the âAccess Keyâ to the component before calling the method to update the fee on the validator.
As you can hopefully see from the example above, this blueprint allows teams to share the responsibility for managing a validator, without having to pass the owner badge between signers. This is particularly useful in the case where an automatic failover might need the use of a hot wallet, whilst safeguarding the custody of the validator owner badge. The âAccess Keyâ badge can be held by the hot wallet and call the component methods, whereas the âAccess Manager Ownerâ badge can be secured separately in a hardware wallet.
Whilst the example shown here relates to validator management, this blueprint can be used wherever a single NFT providing authorisation to component methods needs to be delegated to multiple users.
Revoking Access
Naturally it may be necessary to revoke access to the validator. This is made possible as the âAccess Keyâ badges are recallable by the âAccess Manager Ownerâ badge. Access Keys can also be burned if required. The example below shows how an âAccess Keyâ badge can be recalled and burned.
- Firstly, the vault containing the âAccess Keyâ badge needs to be determined. This is obtained using the following Gateway API end point with the body shown:
https://mainnet.radixdlt.com/state/entity/page/non-fungible-vaults/
{
"address": "account_rdx12yugeppnu2sul2qnry7nscpc9aglm922ygzkvvplp37p3rwvr4z7xz",
"resource_address": "resource_rdx1n2ufdpyuundgtu68nvfdygyj74n7sqs32lsu76smvnt8j3v9976tmw"
}
Note: for full details of this Gateway API call, refer to the Gateway documentation
The above API call returns the vault address containing the âAccess Keyâ badge. In this example, the vault is:
internal_vault_rdx1nzk0lnsld67t4vwzg2345ad03am5tnnw8rgeztt2czs8v4h72hcfga
- In order to now recall and burn this âAccess Keyâ badge, the following manifest should be used:
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"create_proof_of_non_fungibles" Address("resource_rdx1n2r24wvth7ul7jjqtnpm6s9a52lfq7a32lx6ez9chrjq4mgsp8cf6e")
Array<NonFungibleLocalId>(NonFungibleLocalId("{6f654d2d58611380-a3b3b32537ca549f-02b25f6411497e41-b1de3f49f2dadedb}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"recall_key_badge"
Address("internal_vault_rdx1nzk0lnsld67t4vwzg2345ad03am5tnnw8rgeztt2czs8v4h72hcfga");
TAKE_NON_FUNGIBLES_FROM_WORKTOP
Address("resource_rdx1n2ufdpyuundgtu68nvfdygyj74n7sqs32lsu76smvnt8j3v9976tmw")
Array<NonFungibleLocalId>(NonFungibleLocalId("{33a834fe1e62c7ce-d5ed40abff224b74-6dd84f924959af76-d78010d7932dd06b}"))
Bucket("access_key_badge_bucket");
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"burn_key_badge"
Bucket("access_key_badge_bucket");
The manifest above creates a proof of the âAccess Manager Ownerâ badge and presents this to the component. The ârecall_key_badgeâ method is used with the internal vault address we determined in the previous step which passes the âAccess Keyâ badge into a bucket. Finally, the âburn_key_badgeâ method is called which destroys the âAccess Keyâ badge.
Retrieval of the Validator Owner Badge
- In the case that the validator owner badge needs to be retrieved from the component and returned to the validator owner, the following manifest can be used:
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"create_proof_of_non_fungibles" Address("resource_rdx1n2r24wvth7ul7jjqtnpm6s9a52lfq7a32lx6ez9chrjq4mgsp8cf6e")
Array<NonFungibleLocalId>(NonFungibleLocalId("{6f654d2d58611380-a3b3b32537ca549f-02b25f6411497e41-b1de3f49f2dadedb}"));
CALL_METHOD
Address("component_rdx1crkh4nn47vug24t26xfp6sqt28acx8qtwk9sg73rcn5tht5d34a860")
"withdraw_auth_badge";
CALL_METHOD
Address("account_rdx128zppem3e77cwc7j9faw0j79rd627vflvt7mkqgahx728chplp2rey")
"deposit_batch"
Expression("ENTIRE_WORKTOP");
This manifest provides a proof of the âAccess Manager Ownerâ badge before calling the âwithdraw_auth_badgeâ method. The validator owner badge is then deposited directly back into the account shown.
Myself and Ahmed hope that this blueprint may be of use to the community, particularly those with multiple users managing their validator node. If you have any questions about itâs use or suggestions for improvements, please do leave your feedback in reply or contact me on Telegram (@Radstakes).