Skip to content

Commit

Permalink
docs: permissioned contract deployment with tooling
Browse files Browse the repository at this point in the history
based on dapp-agoric-basics

 - style: markdown lint
  • Loading branch information
dckc committed Mar 18, 2024
1 parent c7de7f0 commit 7373869
Show file tree
Hide file tree
Showing 4 changed files with 364 additions and 165 deletions.
10 changes: 5 additions & 5 deletions main/guides/coreeval/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ permissionless [contract installation with Zoe](/guides/zoe/#contract-installati
is limited to development environments.

Until then, permission to deploy contracts can be granted using an Agoric extension to [Cosmos SDK Governance](https://hub.cosmos.network/main/delegators/delegator-guide-cli.html#participating-in-governance) called `swingset.CoreEval`. As discussed in [governance using Hardened JavaScript: swingset\.CoreEval](https://community.agoric.com/t/bld-staker-governance-using-hardened-javascript-swingset-coreeval/99),
if such a proposal passes, its JavaScript code is run with ocaps extracted using the proposal's declared capabilities, which the code can combine to perform privileged tasks.
if such a proposal passes, its JavaScript code is run with ocaps extracted using the proposal's permitted capabilities, which the code can combine to perform privileged tasks.

To do try it out in a local testnet chain:
To make a proposal to deploy a contract:

1. [Declare the capabilities that the proposal will require](./permissions).
2. [Code the proposal itself](./proposal).
3. [Deploy the proposal to a local testnet](./local-testnet).
1. [Write JavaScript code to deploy the contract](./proposal.md).
2. [Declare the capabilities that the script requires](./permissions.md).
3. [Submit transactions to install and deploy the contract](./local-testnet.md).
211 changes: 178 additions & 33 deletions main/guides/coreeval/local-testnet.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,190 @@
# Deploy a Governance Proposal to a Local Testnet
# Deploying Contracts using Core Eval Proposals

To create, start, and deploy an Agoric governance proposal to a local Agoric Testnet, do the following:
1. Check access and governance norms for your target network.
2. Build the transaction contents.
3. Sign and broadcast the transactions.

1. If you're using a Mac, ensure that you have [Xcode](https://apps.apple.com/us/app/xcode/id497799835) installed.
2. Create a project folder that will allow you to run a local blockchain:
For a local agoric blockchain:

```jsx
agoric init <The name of your project>
```

For example, if you wanted to run your local blockchain from a folder named "Demo", you'd run this command:

```jsx
agoric init Demo
```
1. Ensure that you have [started your local agoric blockchain](../getting-started/#starting-a-local-agoric-blockchain):
use `yarn docker:logs` to check that your chain is running; if not, use `yarn start:docker` to start it.

**Note:** Your project folder should *not* be located within your local clone of the agoric SDK.
2. Build the transaction contents with `cd contract; yarn build:deployer`; _details below_.

3. Install additional dependencies by entering your project folder and running the following command.
3. Sign and broadcast the transactions with `yarn start`; _details below_.

```jsx
cd <Name of your project (e.g., Demo)>
agoric install
```
For testnet access and governance norms, see [Guide to Agoric Testnets \- Agoric Community Forum](https://community.agoric.com/t/guide-to-agoric-testnets/515).

4. Start the chain by running the following command. (**Note:** You should still be located within your project folder.)
_Mainnet deployment is out of scope of this document; see [Mainnet 2 Deployment Checklist #8280](https://github.com/Agoric/agoric-sdk/discussions/8280) in agoric-sdk discussions._

```jsx
agoric start local-chain --verbose --reset
```
## Bundling Core Eval Scripts with `build:deployer` and `rollup.config.mjs`

5. Wait for the first block to be produced.
6. Open a second terminal.
7. Within the second terminal, navigate to `<agoric-sdk>/bin` and submit the governance proposal by running the following command. (Make sure to enter "y" when asked to confirm the transaction.)
Core eval scripts are evaluated, not loaded as modules; so any `import` or `export` declarations are syntax errors. In **dapp-agoric-basics**, we use [rollup](https://rollupjs.org/) to support developing
a core-eval script and permit as a module:

```
./agd --chain-id=agoriclocal --title=<Insert your own title> --description=<Insert your description> --home=<PATH to your project folder>/_agstate/keys --keyring-backend=test --from=provision tx gov submit-proposal swingset-core-eval <PATH to permissions> <PATH to proposal>
```
- `import { E } from '@endo/far'`
A bundle strips this declaration during bundling
since the core-eval scope includes exports of `@endo/far`.
- `bundleID = ...` is replaced using updated/cached bundle hash
- `main` is appended as the script completion value
- the `permit` export is emitted to a `.json` file

For example, to deploy the PSM proposal referenced in the previous topics, run the following:
See [rollup.config.mjs](https://github.com/Agoric/dapp-agoric-basics/blob/dc-vote-fee/contract/rollup.config.mjs) for details on using the
`moduleToScript`, `configureBundleID`, and `emitPermit` rollup plug-ins.

```
./agd --chain-id=agoriclocal --title=<Insert your own title> --description=<Insert your description> --home=<PATH to your project folder>/_agstate/keys --keyring-backend=test --from=provision tx gov submit-proposal swingset-core-eval ../packages/inter-protocol/test/psm/gov-add-psm-permit.json ../packages/inter-protocol/test/psm/gov-add-psm.js
```
Running it looks something like this:

```console
dapp-agoric-basics/contract$ yarn build:deployer
yarn run v1.22.19
$ rollup -c rollup.config.mjs

./src/sell-concert-tickets.proposal.js → bundles/deploy-sell-concert-tickets.js...
bundles add: sell-concert-tickets from ./src/sell-concert-tickets.contract.js
bundles bundled 141 files in bundle-sell-concert-tickets.js at 2024-03-09T16:22:08.531Z
created bundles/deploy-sell-concert-tickets.js in 2.4s
...

dapp-agoric-basics/contract$ cd bundles
dapp-agoric-basics/contract/bundles$ ls deploy-sell* bundle-sell*
bundle-sell-concert-tickets.js deploy-sell-concert-tickets-permit.json deploy-sell-concert-tickets.js
```

## Sending Transactions to Deploy a Contract

To submit transactions to get the contract and core eval script and permit on chain, you can use a web UI or CLI tools.

### Ensuring you have enough IST to install a bundle

Installing a contract costs 0.02IST per kilobyte.

On a local chain, use `yarn docker:make mint100` to get 100 IST, which should be enough. _`mint100` works by sending some ATOM from a "whale" account and using the ATOM to open a vault._

On devnet, use the [devnet faucet](https://devnet.faucet.agoric.net/) to get 25 IST. You might need to use it 2 or 3 times.

### Installing, submitting with **cosgov** Web UI

The Cosmos Proposal Builder at [cosgov.org](https://cosgov.org) is one way to submit the transactions. It supports the local chain as well as **devnet** etc.

You'll need a `.json` formatted bundle:

```console
dapp-agoric-basics/contract$ yarn bundle-source --cache-json bundles/ src/sell-concert-tickets.contract.js sell-concert-tickets
bundles/ add: sell-concert-tickets from src/sell-concert-tickets.contract.js
bundles/ bundled 141 files in bundle-sell-concert-tickets.json at 2024-03-09T20:48:43.784Z
```

<img src='../assets/cosgov-install-bundle.png' alt='Install Bundle screenshot' width="600" />

Then drop your permit and core eval script on the **CoreEval Proposal** tab:

<img src='../assets/cosgov-core-eval.png' alt='Install Bundle screenshot' width="600" />

To vote for the propsal, use something like:

```sh
PROPOSAL=13
agd tx gov vote $PROPOSAL yes --keyring-backend test --chain-id agoriclocal --from validator --broadcast-mode block --gas auto --gas-adjustment 1.4 --yes --output json
```

_Note: the voting window on the agoriclocal chain is very short: 10 seconds._

### Installing, submitting with `scripts/deploy-contract`

In **dapp-agoric-basics**, [contract/scripts/deploy-contract.js](https://github.com/Agoric/dapp-agoric-basics/blob/dc-vote-fee/contract/scripts/deploy-contract.js) automates much of the process. `yarn start` uses `yarn docker:make` to run it inside the container, where `agd` is available. The `--install src/sell-concert-tickets.contract.js` option tells it to install the contract, after bundling it if necessary:

```
dapp-agoric-basics/contract$ yarn start
yarn run v1.22.19
$ yarn docker:make clean start-contract print-key
$ docker compose exec agd make -C /workspace/contract clean start-contract print-key
make: Entering directory '/workspace/contract'
yarn node ./scripts/deploy-contract.js --service . \
--install src/sell-concert-tickets.contract.js \
--evals ... ./src/sell-concert-tickets.proposal.js
yarn node v1.22.21
bundles add: sell-concert-tickets from src/sell-concert-tickets.contract.js
bundles bundled 141 files in bundle-sell-concert-tickets.js at 2024-03-09T16:49:54.859Z
installing sell-concert-tickets b1-8dd96
$$$ agd tx swingset install-bundle @bundles/bundle-sell-concert-tickets.json --gas auto --keyring-backend test --chain-id agoriclocal --from agoric1uddt8l5y2sfanzal42358az5dus563f2wk7ssm --broadcast-mode block --gas auto --gas-adjustment 1.4 --yes --output json
gas estimate: 53922087
{
id: 'b1-8dd96',
installTx: 'F18DF8CE296D29BD0283780C402D7C3D22345E13DD04D6B12CEE6AE5B4B9212B',
height: '1594'
}
follow { delay: 2 } ...
{
name: 'sell-concert-tickets',
id: 'b1-8dd96',
installHeight: '1594',
installed: true
}
...
```

As a by-product, it writes a `bundles/bundle-sell-concert-tickets.json.installed` file. If you run `yarn start` again, `deploy-contract.js` uses the `Makefile` to check whether the `.installed` file is up-to-date and only repeats `agd tx swingset install-bundle` if you change the contract.

The `--evals ./src/sell-concert-tickets.proposal.js` option tells it to submit a `swingset-core-eval` governance propsal, after building the script and permit if necessary:

```console
...
submit proposal sell-concert-tickets
[
'bundles/deploy-sell-concert-tickets-permit.json',
'bundles/deploy-sell-concert-tickets.js'
]
await tx [
'bundles/deploy-sell-concert-tickets-permit.json',
'bundles/deploy-sell-concert-tickets.js'
]
$$$ agd tx gov submit-proposal swingset-core-eval bundles/deploy-sell-concert-tickets-permit.json bundles/deploy-sell-concert-tickets.js --title sell-concert-tickets --description sell-concert-tickets --deposit 10000000ubld --keyring-backend test --chain-id agoriclocal --from agoric1jkfphfd8fd7vd7erdttne3k4c0rucu8l22ndhk --broadcast-mode block --gas auto --gas-adjustment 1.4 --yes --output json
gas estimate: 1443920
{
txhash: '010C912FFC47AE59C33A36906DA5096C8EA8A64247699ABF958F58600E57C59E',
code: 0,
height: '1626',
gas_used: '1028352'
}
...
```

The `deploy-contract.js` automates adding a deposit and voting on the proposal and waiting for voting to complete:

```
...
await voteLatestProposalAndWait [
'bundles/deploy-sell-concert-tickets-permit.json',
'bundles/deploy-sell-concert-tickets.js'
]
{ before: 'deposit', on: '13', delay: 1 } ...
$$$ agd tx gov deposit 13 50000000ubld --keyring-backend test --chain-id agoriclocal --from validator --broadcast-mode block --gas auto --gas-adjustment 1.4 --yes --output json
gas estimate: 418391
$$$ agd tx gov vote 13 yes --keyring-backend test --chain-id agoriclocal --from validator --broadcast-mode block --gas auto --gas-adjustment 1.4 --yes --output json
gas estimate: 105278
Waiting for proposal 13 to pass (status=PROPOSAL_STATUS_VOTING_PERIOD)
...
Waiting for proposal 13 to pass (status=PROPOSAL_STATUS_PASSED)
13 2024-03-09T16:50:36.973940196Z PROPOSAL_STATUS_PASSED
{ step: 'run', propsal: '13', delay: 1 } ...
{
proposal_id: '13',
content: {
'@type': '/agoric.swingset.CoreEvalProposal',
title: 'sell-concert-tickets',
description: 'sell-concert-tickets',
evals: [ [Object] ]
},
status: 'PROPOSAL_STATUS_PASSED',
final_tally_result: { yes: '5000000000', abstain: '0', no: '0', no_with_veto: '0' },
submit_time: '2024-03-09T16:50:26.973940196Z',
deposit_end_time: '2024-03-11T16:50:26.973940196Z',
total_deposit: [ { denom: 'ubld', amount: '60000000' } ],
voting_start_time: '2024-03-09T16:50:26.973940196Z',
voting_end_time: '2024-03-09T16:50:36.973940196Z'
}
```

When the proposal passes, `deploy-contract.js` writes a `deploy-sell-concert-tickets.js.done` file to avoid running it again unnecessarily.

Use `yarn docker:logs` to check for any errors in the execution of the core eval script.
123 changes: 41 additions & 82 deletions main/guides/coreeval/permissions.md
Original file line number Diff line number Diff line change
@@ -1,96 +1,55 @@
# Declaring Required Capabilities

Write a json file declaring the capabilities that the proposal will need. For example,
[gov-add-psm-permit.json](https://github.com/Agoric/agoric-sdk/blob/master/packages/inter-protocol/test/psm/gov-add-psm-permit.json)
declares capabilities needed to start a new PSM contract. Note that capabilities are declared using
their name as a property along with any truthy value. For example,`"bankManager": true` and
`"bankManager": 'hello'` are equivalent; they both set the `bankManager` capability.

::: details Show example permissions file
```
{
"consume": {
"agoricNamesAdmin": true,
"bankManager": true,
"board": true,
"chainStorage": true,
"zoe": "zoe",
"feeMintAccess": "zoe",
"economicCommitteeCreatorFacet": "economicCommittee",
"econCharterKit": true,
"provisionPoolStartResult": true,
"psmKit": true,
"chainTimerService": "timer"
},
"produce": {
"testFirstAnchorKit": true
},
"installation": {
"consume": {
"contractGovernor": true,
"psm": true,
"mintHolder": true
}
},
"instance": {
"consume": {
"economicCommittee": true
}
Most contract deployments don't need everything in `BootstrapPowers`.
Verifying by inspection that they don't use any more than they need
is notoriously difficult. So proposals come with
a `BootstrapManifestPermit` to declare an upper limit on the capabilities they access. For a property access `powers.P`, if the permit has `{ P: true }`, then the access succeeds. In fact, any _truthy_ value will do.
And recursively, a property access `powers.P.Q.R` is succeeds if
the permit has `{ P: { Q: { R: true } } }`.

The permit for `startSellConcertTicketsContract` is:

```js
/** @type { import("@agoric/vats/src/core/lib-boot").BootstrapManifestPermit } */
export const permit = harden({
consume: {
agoricNames: true,
brandAuxPublisher: true,
startUpgradable: true, // to start contract and save adminFacet
zoe: true, // to get contract terms, including issuer/brand
},
"issuer": {
"produce": {
"DAI": true
},
"consume": {
"DAI": true
}
installation: {
consume: { [contractName]: true },
produce: { [contractName]: true },
},
"brand": {
"consume": {
"DAI": true,
"IST": true
},
"produce": {
"DAI": true
}
}
}
```
:::

## Top Level Consume Section

In this section you need to set all the permissions that your contract will need to be able to use
(i.e., "consume"). Some of the listed permissions in the example PSM permission file above are of
general interest to most contracts, while others are more specific to the PSM contract.
instance: { produce: { [contractName]: true } },
issuer: { consume: { IST: true }, produce: { Ticket: true } },
brand: { consume: { IST: true }, produce: { Ticket: true } },
});

* **agoricNamesAdmin**: Grants write access to the Agoric name service. This permission is somewhat specific to the PSM contract.
* **bankManager**: Grants access to bank-related functionality within ERTP, allowing the contract to manipulate nearly all Cosmos assets in the chain. Because this capability is very powerful, this permission should only be granted to contracts that absolutely need it.
* **board**: Grants write access to the [board name service](/guides/wallet/index#the-agoric-board).
* **chainStorage**: Grants write access to the chain storage node, which is required when running `agd query` commands. Thus, most contracts will need access to this.
* **zoe**: When this permission is set, it grants access to the Zoe framework. All contracts will need access to this.
* **feeMintAccess**: When this permission is set, the contract will be able to create digital assets. Only contracts that mint privileged Agoric digital assets (i.e., not the unprivileged **[zcf.makeZCFMint()](/reference/zoe-api/zoe-contract-facet#zcf-makezcfmint-keyword-assetkind-displayinfo)**) will need access to this.
* **economicCommitteeCreatorFacet**, **econCharterKit**, **provisionPoolStartResult**: These 3 permissions are required by governed contracts.
* **chainTimerService**: When this permission is set, it grants access to the *chainTimerService*. All governed contracts need access to this so they know when a vote is complete.

## Top Level Produce Section
export const main = startSellConcertTicketsContract;
```

Specifies what, if anything, the contract produces. For example, the example PSM contract
produces *testFirstAnchorKit* which is used for testing purposes.
## Selected BootstrapPowers

## Installation Section
In the top level promise space, we have:

Specifies what well-known installations the contract requires. At a minimum, the contract itself should
be listed as an installation. Governed contracts should include a *contractGovernor* installation.
- **agoricNames**: read-only access to the [agoricNames](../integration/name-services.md#agoricnames-agoricnamesadmin-well-known-names) name service.

## Instance Section
- **board**: the [board](../integration/name-services.md#the-board-publishing-under-arbitrary-names) name service.
**Note: the board only grows; no mechanism to reclaim storage has been established.**

Specifies what instances, if any, the contract produces or consumes.
- **chainStorage**: to make storage nodes to write to vstorage.
**Warning: this includes the right to over-write previously allocated storage nodes.**

## Issuer Section
- **chainTimerService**: for getting the current timer and setting timer wake-ups; for example, at the conclusion of a governance vote.
**Note: this includes the right to schedule infinitely repeating events.**

Specifies what **[Issuers](/reference/ertp-api/issuer)**, if any, the contract produces or consumes.
- **agoricNamesAdmin**: admin / update access to **agoricNames** and the name hubs it contains.
**Warning: this includes the right to over-write existing bindings to instances, brands, etc.**

## Brand Section
- **bankManager**: to manage reflection of cosmos
assets as ERTP assets: to register an issuer to correspond to a denom or to get a bank of purses for any address. **Warning: this includes the right to spend assets for any account.**

Specifies what **[Brands](/reference/ertp-api/brand)**, if any, the contract produces or consumes.
- **zoe**: the Zoe service.
**Note: this includes the right to install any available bundle and start instances of any available installation.**
Loading

0 comments on commit 7373869

Please sign in to comment.