Skip to content

Commit

Permalink
Adding a simple view function to demonstrate the upgradeability of th…
Browse files Browse the repository at this point in the history
…e contract. Updated the tutorial to show how to use it. The tutorial shows how to use the newly added `build-publish-package` function to get the bytecode in a JSON file and run the upgrade_contract function with it.
  • Loading branch information
xbtmatt committed Jul 13, 2023
1 parent a689b3e commit 8f4e81b
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,9 @@ module upgrade_resource_contract::upgrader {
code,
);
}

#[view]
public fun upgradeable_function(): u64 {
9000
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"function_id": "be326762ddd27624743223991c2223027621e62b7d0849a40a970fa2df385da9::upgrader::upgrade_contract",
"type_args": [],
"args": [
{
"type": "hex",
"value": "0x2155706772616465205265736f75726365204163636f756e7420436f6e747261637401000000000000000040423434313945414132343332454643453639443743433842333935364336454333363833344536343044434244314437343732323334363743434635414545329d011f8b08000000000002ff3d8dcd0ac2301084ef798a25775b5fc043117c00c15329b26ed6129a66437e1411dfdd04b1cc5c86f9861903d282334fcae3ca70007d097344c370e6242512c34024c567388acf11296bf5e098acf846efbb2aadca6f740de22cbd5a41b206ccf6e6582b351a0eec0d7bb29c2635842ce914ebe153e252e9373821746dd7757df5fd5ff6d8d8dd96357cbe9710eb1ab3000000010875706772616465729d041f8b08000000000002ff8d534d6fdb300cbdf7577018d0d94090a6c0306c4e5314d86987edb2e330188ccc24421dc9d347d2acc87f9fa428f2479d603ec9241f1ff9f4b49595ad096cb3565851a9484bab18954c0aa39099a2882905af37e03eab09b4a98a42f3b52035ef079131698543bdfe0ce9afd8e092d7dc1c8e83cac41421e38d0651262b9adf849836ca3203df0f4322d8a086673ac479fd97b84e33970c9b0286b849283f9ebaafac002eb829b7419f6cd0a180dbd321efb0a0d6a4ccbb2c961458550ea64bb91ac273582ce0e9a2e61398e5f3d4b62633b6002c6028a117d5284ebb4ecf98f288e1141378927b3f4dcbb5950e6be4dbca1199dbc52f4a3c129c24d431f246c5efeee0873404664351492e85fb4313420cebda7990eb38f414bead604f4e1ddc79904bd45c1048ebe4437190c263c4d9d8a71e495f7a69a4e6621dc2e729216af541a79076adaa1ed63b83f9d1d08b300da33776597306e42a0ec139e79b3d83b2b474983db9a715634b062b3458bac5b96bfd97aa0276c48c540ff6f3635be85f40cab405b122773bfcb1dc8d7ffdc6ae58f5648960d0709cc0fd7f99f176299592fb725dcb25d60f6fe91fb3cb8ecfa7234dafb23ac6e47aa6084d42eeb9d97878a4cd463a77160a7a16e106f5a66c903de3da3d811791f5ec7d3b7c11bdecc8edf50b3c4b1be91bfffdaf1da7fdefae933a1ec2654de5d973595e80fdf4b173915f66b3fbd8ecf80f9d1d74aacb05000000000300000000000000000000000000000000000000000000000000000000000000010e4170746f734672616d65776f726b00000000000000000000000000000000000000000000000000000000000000010b4170746f735374646c696200000000000000000000000000000000000000000000000000000000000000010a4d6f76655374646c696200"
},
{
"type": "hex",
"value": [
"0xa11ceb0b060000000b01000a020a0803122305351d0752f30108c502400685034410c9032f0af803060cfe036e0dec04020000010101020103010400050800010a0600000600010000070201000008010300040b000500030c060400010d080700020e02010001060c0003060c0a020a0a020103010801010502060c05010c01060801087570677261646572076163636f756e7404636f6465107265736f757263655f6163636f756e74067369676e6572124d795369676e65724361706162696c6974790b696e69745f6d6f64756c6510757067726164655f636f6e7472616374147570677261646561626c655f66756e6374696f6e137265736f757263655f7369676e65725f636170105369676e65724361706162696c6974790a616464726573735f6f661d72657472696576655f7265736f757263655f6163636f756e745f6361701d6372656174655f7369676e65725f776974685f6361706162696c697479137075626c6973685f7061636b6167655f74786e5189d8b84793e93065f28893ab3e01cf5883e5daf93404d567a79be29958e423000000000000000000000000000000000000000000000000000000000000000105205189d8b84793e93065f28893ab3e01cf5883e5daf93404d567a79be29958e4230520f43c3dbad12a67d2c242a3e3bbbc8718cde26b36f75c075e96bf55dda9b0af76126170746f733a3a6d657461646174615f76311b000001147570677261646561626c655f66756e6374696f6e0101000002010908010000000004130a0011030700210406050a0b0001060000000000000000270a00070111040c010b000b0112002d0002010104010007120b001103070121040605080601000000000000002707002b00100011050c030e030b010b0211060202010000010206292300000000000002000000"
]
}
]
}
152 changes: 140 additions & 12 deletions developer-docs-site/docs/concepts/resource-accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ id: "resource-accounts"

A resource account is an [Account](https://aptos.dev/concepts/accounts/) that's used to store and manage resources. It can be a simple storage account that's used merely to separate different resources for an account or a module, or it can be utilized to programmatically control resource management in a contract.

There are two distinct ways to manage a resource account. In this guide we'll discuss how to implement each technique, variations on the implementations, and any configuration details relevant to the creation process.
There are two distinct ways to manage a resource account:

## How to utilize a resource account
1. The auth key is rotated to a separate account that can control it manually
2. The Move VM rotates the auth key to 0x0 and controls the account through a SignerCapability

### Manually controlling a resource account by rotating its auth key to another account's auth key
In this guide we'll discuss how to implement each technique, variations on the implementations, and any configuration details relevant to the creation process.

## Rotating the auth key to another account

The first technique we're going to discuss is through the `create_resource_account` function in the `resource_account.move` contract. View the code [here](https://github.com/aptos-labs/aptos-core/blob/4beb914a168bd358ec375bfd9854cffaa271199a/aptos-move/framework/aptos-framework/sources/account.move#L602).

Expand Down Expand Up @@ -39,7 +42,7 @@ Notice that there is nothing returned here- we are not given anything to store o
If you don't specify an auth key, that is, if you pass in `vector::empty<u8>()` or `vector<u8> []` to the `optional_auth_key` field, it will automatically rotate the auth key to the `origin` account's auth key.
:::

### Managing a resource account programmatically with a SignerCapability
## Rotating the auth key to 0x0 to create a SignerCapability

The second technique is the `create_resource_account` function in the `account.move` contract. View the code [here](https://github.com/aptos-labs/aptos-core/blob/4beb914a168bd358ec375bfd9854cffaa271199a/aptos-move/framework/aptos-framework/sources/account.move#L602).

Expand Down Expand Up @@ -110,7 +113,7 @@ To intuitively understand why a `SignerCapability` is allowed to be so powerful,
Upon creating the `SignerCapability`, you're free to decide how you want to expose it. You can store it somewhere, give it away, or gate its access to functions that use it or conditionally return it.
:::

### Using a resource account to publish a module
## Publishing a module to a resource account

There are a few other ways we can utilize a resource account. One common usage is to use it to publish a module:

Expand Down Expand Up @@ -141,7 +144,7 @@ If you don't store the `SignerCapability` there is no way to retrieve the resour
You *also* need to provide some way to use or retrieve the `SignerCapability`, too, or you won't even be able to use it.
:::

### Publishing an upgradeable module with a resource account
## Publishing an upgradeable module to a resource account

If you want to publish to a resource account and also have an upgradeable contract, use the `init_module` function to use the resource account's signer to retrieve and store the `SignerCapability`. Here's a full working example:

Expand Down Expand Up @@ -180,6 +183,11 @@ module upgrade_resource_contract::upgrader {
code,
);
}

#[view]
public fun upgradeable_function(): u64 {
9000
}
}
```

Expand All @@ -195,13 +203,133 @@ aptos move create-resource-account-and-publish-package --address-name upgrade_re

Where `CONTRACT_DEPLOYER` is the profile. Read more about [Aptos CLI profiles here](https://aptos.dev/tools/aptos-cli-tool/use-aptos-cli/#creating-other-profiles).

If you want to see an end to end unit test displaying how to publish and then upgrade the code above by calling `upgrade_contract`, you can run the cargo test:
Let's run through an example of how to publish the above upgradeable contract to a resource account and upgrade it.

1. Publish the module to a resource account
2. Run the `upgradeable_function` view function and see what it returns
3. Upgrade the module using the json output from the `aptos move build-publish-package` command
4. Run the `upgradeable_function` view function again to see the new return value

First make sure you have a default profile initialized to devnet.

```shell
aptos init --profile default
```

Choose `devnet` and leave the private key part empty so it will generate an account for you. When we write `default` in our commands, it will automatically use this profile.

Navigate to the `move-examples/upgrade_resource_contract` directory.

### Publish the module

```shell
aptos move create-resource-account-and-publish-package --address-name upgrade_resource_contract --seed '' --named-addresses owner=default
```

The `--address-name` flag denotes that the resource address created from the resource account we make will be supplied as the `upgrade_resource_contract` address in our module. Since we declared it as the module address with `module upgrade_resource_contract::upgrader { ... }` at the very top of our contract, this is where our contract will be deployed.

When you run this command, it will ask you something like this:

```
Do you want to publish this package under the resource account's address be326762ddd27624743223991c2223027621e62b7d0849a40a970fa2df385da9? [yes/no] >
```

Say yes and copy that address to your clipboard. That's our resource account address where the contract is deployed.

Now you can run the view function!

### Run the view function

```shell
aptos move view --function-id RESOURCE_ACCOUNT_ADDRESS::upgrader::upgradeable_function
```

Remember to replace `RESOURCE_ACCOUNT_ADDRESS` with the resource account address you deployed your module to; it is different from the one posted above, so this will specifically only work for *your* contract.

It should output:
```json
Result: [
9000
]
```

### Change the view function

Now let's change the value returned in the view function from `9000` to `9001` so we can see that we've upgraded the contract:

```rust
#[view]
public fun upgradeable_function(): u64 {
9001
}
```

Save that file, and then use the `build-publish-package` command to get the bytecode output in JSON format.

### Get the bytecode for the module

```shell
cargo test --package e2e-move-tests --lib -- tests::upgrade_resource_contract::code_upgrading_using_resource_account --exact --nocapture
aptos move build-publish-payload --json-output-file upgrade_contract.json --named-addresses upgrade_resource_contract=RESOURCE_ACCOUNT_ADDRESS,owner=default
```

Replace `RESOURCE_ACCOUNT_ADDRESS` with your resource account address and run the command. Once you do this, there will now be a `upgrade_contract.json` file with the bytecode output of the new, upgraded module in it.

The hex values in this JSON file are arguments that we'd normally use to pass into the `0x1::code::publish_package_txn` function, but since we made our own `upgrade_contract` function that wraps it, we need to change the function call value to something else.

Your JSON should look something like the below output, just with expanded `value` fields (truncated here for simplicity's sake):

```json
{
"function_id": "0x1::code::publish_package_txn",
"type_args": [],
"args": [
{
"type": "hex",
"value": "0x2155...6200"
},
{
"type": "hex",
"value": [
"0xa11c...0000"
]
}
]
}
```

Change the `function_id` value in the JSON file to match your contract's upgrade function contract, with your resource account address filled in:

```
"function_id": "RESOURCE_ACCOUNT_ADDRESS::upgrader::upgrade_contract",
```

### Creating and funding a resource account
Save this file so we can use it to run an entry function with JSON parameters.

### Run the upgrade_contract function

```shell
aptos move run --json-file upgrade_contract.json
```

Confirm yes to publish the upgraded module where the view function will return 9001 instead of 9000.

### Run the upgraded view function

```shell
aptos move view --function-id RESOURCE_ACCOUNT_ADDRESS::upgrader::upgradeable_function
```

You should get:

```json
Result: [
9001
]
```

Now you know how to publish an upgradeable module to a resource account!

## Creating and funding a resource account

Another common usage is to create and fund a resource account, in case the account needs access to functions that need access to `Coin<AptosCoin>`:

Expand All @@ -225,9 +353,9 @@ public entry fun create_resource_account_and_fund(
}
```

#### Can I acquire a SignerCapability later?
## Acquiring a SignerCapability later

Yes. Say you create a resource account and rotate its auth key to your account's auth key. You'd just need to sign for the account and call `retrieve_resource_account_cap` in order to get the `SignerCapability` and store it somewhere:
Say you create a resource account and rotate its auth key to your account's auth key. You'd just need to sign for the account and call `retrieve_resource_account_cap` in order to get the `SignerCapability` and store it somewhere:

```rust
struct MySignerCapability has key {
Expand All @@ -248,7 +376,7 @@ Call the function, but change the sender account to appear as the resource accou
aptos move run --function-id MODULE_ADDRESS::MODULE_NAME::retrieve_cap --args address:default --sender-account RESOURCE_ADDRESS_HERE --profile default
```

#### How is the address for a resource account derived?
## How is the address for a resource account derived?

When a resource account is created, the address is derived from a SHA3-256 hash of the requesting account's address plus an optional byte vector `seed`. If you want to know the resource address generated by an account + a given arbitrary seed, you can call the `create_resource_address` function in `account.move`:

Expand Down

0 comments on commit 8f4e81b

Please sign in to comment.