Skip to content

Commit 10d6ceb

Browse files
Merge pull request #542 from ProvableHQ/issue-cleanup/11-7-25
Cleaning up GH Issues
2 parents 5726e87 + 597d664 commit 10d6ceb

File tree

13 files changed

+1105
-151
lines changed

13 files changed

+1105
-151
lines changed

documentation/guides/01_async.md

Lines changed: 99 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,140 @@ sidebar_label: Async Model
77

88
## Background
99

10-
The Leo asynchronous programming model enables users to update public on-chain data using a developer-friendly syntax. A function call to on-chain code is treated as an async function call which returns a `Future` object. A `Future` contains a description of a call to a function: it consists of the name of the function and of the values passed as arguments. The execution of the on-chain state change occurs after validators verify the proof associated with the transaction.
10+
The Leo asynchronous programming model enables users to update public on-chain data using a developer-friendly syntax.
11+
12+
The execution of on-chain code is treated as an `async function` call which returns a `Future` object. The execution of the on-chain state change occurs after validators verify the proof associated with the transaction.
1113

1214

1315
## Managing Public State
1416

15-
On-chain data is stored in public mappings. Any logic that reads from or updates the state of a mapping must be contained within an `async function` block as follows:
17+
On-chain data is stored publicly in one of three data structures: mappings, storage variables, and storage vectors. Any logic that reads from or updates the state of these structures must be contained within an `async function` block as follows:
1618

1719
```leo
18-
program first_mapping.aleo {
20+
program first_public_state.aleo {
1921
mapping accumulator: u8 => u64;
22+
storage count: u8;
23+
storage queue: [u8];
2024
21-
async function finalize_increment_state(){
22-
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, default 0
25+
async function increment_state_onchain(){
26+
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, defaults to 0
2327
let new_count: u64 = current_count + 1u64;
2428
accumulator.set(0u8, new_count);
2529
}
30+
31+
async function increment_count_onchain(){
32+
let current_count: u8 = count.unwrap_or(0u8); // Get current value, defaults to 0
33+
count = current_count + 1u8;
34+
}
35+
36+
async function add_to_queue_onchain(val: u8){
37+
queue.push(val); // Push to end of queue
38+
}
2639
}
2740
```
2841

29-
However, users can only call `transition` functions. In order for the Future generated by an `async function` to be usable, it must be returned by a `transition` function. Any `transition` that calls on an `async function` within its block must be annotated with the `async` keyword and must explicitly return a `Future`. Async transitions can return additional data types in a tuple, including Records, along with a `Future`. Only one `Future` can be returned. If multiple types are returned, the `Future` must be the last type in the tuple.
42+
However, as users can only call `transition` functions, the `Future` generated by an `async function` must be returned from a `transition` in order to be usable. Any `transition` that invokes this process must be annotated with the `async` keyword. There are also a few other nuances:
43+
- Async transitions can return additional data types in a tuple, including Records, along with a `Future`.
44+
- Only one `Future` can be returned.
45+
- If multiple types are returned, the `Future` must be the last type in the tuple.
3046
```leo
31-
program first_mapping.aleo {
47+
program first_public_state.aleo {
3248
mapping accumulator: u8 => u64;
33-
34-
async transition_increment() -> Future {
35-
return finalize_increment_state();
49+
storage count: u8;
50+
storage queue: [u8];
51+
52+
//=============================================================
53+
// MAPPING MODIFICATION
54+
//=============================================================
55+
async transition increment_accumulator() -> Future {
56+
return increment_state_onchain();
3657
}
37-
38-
async function finalize_increment_state(){
39-
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, default 0
58+
async function increment_accumulator_onchain(){
59+
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, defaults to 0
4060
let new_count: u64 = current_count + 1u64;
4161
accumulator.set(0u8, new_count);
4262
}
63+
64+
//=============================================================
65+
// STORAGE VARIABLE MODIFICATION
66+
//=============================================================
67+
async transition increment_count() -> Future {
68+
return increment_count_onchain();
69+
}
70+
async function increment_count_onchain(){
71+
let current_count: u8 = count.unwrap_or(0u8); // Get current value, defaults to 0
72+
count = current_count + 1u8;
73+
}
74+
75+
//=============================================================
76+
// STORAGE VECTOR MODIFICATION
77+
//=============================================================
78+
async transition add_to_queue(val: u8) -> Future {
79+
return add_to_queue_onchain(val: u8);
80+
}
81+
async function add_to_queue_onchain(val: u8){
82+
queue.push(val); // Push to end of queue
83+
}
4384
}
4485
```
4586

4687
Leo also offers a shorthand for writing onchain code in the form of `async` blocks within `async transition` functions.
4788
```leo
48-
program first_mapping.aleo {
89+
program first_public_state.aleo {
4990
mapping accumulator: u8 => u64;
50-
51-
async transition_increment() -> Future {
91+
storage count: u8;
92+
storage queue: [u8];
93+
94+
//=============================================================
95+
// MAPPING MODIFICATION
96+
//=============================================================
97+
async transition increment_accumulator() -> Future {
5298
let f : Future = async {
53-
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, default 0
99+
let current_count: u64 = accumulator.get_or_use(0u8, 0u64); // Get current value, defaults to 0
54100
let new_count: u64 = current_count + 1u64;
55101
accumulator.set(0u8, new_count);
56102
}
57103
return f;
58104
}
59105
106+
//=============================================================
107+
// STORAGE VARIABLE MODIFICATION
108+
//=============================================================
109+
async transition increment_count() -> Future {
110+
let f : Future = async {
111+
let current_count: u8 = count.unwrap_or(0u8); // Get current value, defaults to 0
112+
count = current_count + 1u8;
113+
}
114+
return f;
115+
}
116+
117+
//=============================================================
118+
// STORAGE VECTOR MODIFICATION
119+
//=============================================================
120+
async transition add_to_queue(val: u8) -> Future {
121+
let f : Future = async {
122+
queue.push(val); // Push to end of queue
123+
}
124+
return f;
125+
}
60126
}
61127
```
62128

63129

64130

65-
## Calling Async Transitions from External Programs
131+
132+
## External Async Transitions
66133

67134
Leo enables developers to call external `async transitions` from imported programs in an `async transition`. A call to an async transition returns a `Future` which must be passed as inputs to an async function. These `Future`s must be composed inside of the `async function` using the `await` keyword, as shown in the example below.
68135

69136
```leo
70-
import first_mapping.aleo;
137+
import first_public_storage.aleo;
71138
72-
program second_mapping.aleo {
139+
program second_public_storage.aleo {
73140
mapping hashes: u8 => scalar;
74141
75142
async transition two_mappings(value: u8) -> Future {
76-
let increment_future: Future = first_mapping.aleo/transition_increment();
143+
let increment_future: Future = first_public_storage.aleo/increment();
77144
return finalize_update_mapping(value, imported_future);
78145
}
79146
@@ -87,13 +154,13 @@ program second_mapping.aleo {
87154

88155
If using `async` blocks, you will need to call the external `async transition` outside the block and `await` the resulting `Future` within.
89156
```leo
90-
import first_mapping.aleo;
157+
import first_public_storage.aleo;
91158
92-
program second_mapping.aleo {
159+
program second_public_storage.aleo {
93160
mapping hashes: u8 => scalar;
94161
95162
async transition two_mappings(value: u8) -> Future {
96-
let increment_future: Future = first_mapping.aleo/transition_increment();
163+
let increment_future: Future = first_public_storage.aleo/increment();
97164
let f: Future = async {
98165
imported_future.await();
99166
let hash: scalar = BHP256::hash_to_scalar(value);
@@ -103,14 +170,19 @@ program second_mapping.aleo {
103170
}
104171
}
105172
```
173+
You can access the inputs to an external future using the following syntax:
174+
```leo
175+
let f = imported_program.aleo/some_function();
176+
let value = f.0; // or f.1, f.2, f.3 and so on depending on the input index
177+
```
106178

107179

108-
## Managing Public and Private State in Async Transitions
180+
## Managing Both Public and Private State
109181

110182
Updating private state on Aleo utilizes off-chain proof generation to preserve the confidentiality of the user’s data and associated address. Therefore, Records cannot be created or consumed within the scope of `async functions`. However, Records can be used inside of the scope of `async transitions`. This is because transition and async transition functions are initially executed off-chain and are accompanied by proofs of correct execution which are subsequently verified by validators. Once the proof is verified, validators execute the code contained within a `Future`, which is solely defined by code within an `async function`.
111183

112184
| | **Public State** | **Private State** |
113185
|--------------------------|----------------------|------------------------------------|
114-
| **Function Type** | `async function` | `async transition` or `transition` |
115-
| **Data Storage** | `mapping` | `record` |
186+
| **Function Type** | `async function`, `async` block | `async transition` or `transition` |
187+
| **Data Storage** | `mapping`, `storage` | `record` |
116188
| **Visibility** | everyone | visible if you have the `viewkey` |

documentation/guides/07_devnet.md

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ A local devnet can be a heavyweight but reliable way to test your application on
1111

1212
The Leo CLI provides a helpful command to help startup a local devnet:
1313
```bash
14-
leo devnet --snarkos <SNARKOS>
14+
leo devnet --snarkos <SNARKOS> --snarkos-features test_network
1515
```
1616
The `<SNARKOS>` is the path to an installed binary of [**snarkOS**](https://github.com/ProvableHQ/snarkOS), the decentralized operating system that forms the backbone of the Aleo network.
1717

1818
If you don't have snarkOS installed, you can pass the `--install` flag and the CLI will automatically download, compile, and store the binary at the path specified by `<SNARKOS>`.
1919
```bash
20-
leo devnet --snarkos <SNARKOS> --install
20+
leo devnet --snarkos <SNARKOS> --snarkos-features test_network --install
2121
```
2222
:::info
2323

@@ -35,11 +35,11 @@ Windows users will need to perform some additional steps in order for snarkOS to
3535

3636
The `tmux` command will allow you to toggle between nodes in your local devnet. You can enable this by passing the `--tmux` flag upon startup:
3737
```bash
38-
leo devnet --snarkos <SNARKOS> --tmux
38+
leo devnet --snarkos <SNARKOS> --snarkos-features test_network --tmux
3939
```
4040
:::info
4141
This feature is only available on Unix-based systems.
42-
::
42+
:::
4343

4444
You'll need to install the `tmux` package first:
4545

@@ -87,14 +87,35 @@ See the full `leo devnet` CLI documentation [here](./../cli/07_devnet.md)
8787

8888
## Usage
8989

90+
When you start the devnet, the CLI will actually spin up a new instance of the blockchain from genesis via the snarkOS binary. This means that the chain will start at block 0 and consensus version 1, and the only program deployed will be `credits.aleo`.
91+
92+
The height of the chain will increase as blocks are produced. At various different heights, a new consensus version will activate, which will unlock various features that have been implemented as the Aleo network has matured. By default, the devnet will assume the predefined consensus heights from Testnet:
93+
94+
```rust
95+
(ConsensusVersion::V1, 0),
96+
(ConsensusVersion::V2, 2_950_000),
97+
(ConsensusVersion::V3, 4_800_000),
98+
(ConsensusVersion::V4, 6_625_000),
99+
(ConsensusVersion::V5, 6_765_000),
100+
(ConsensusVersion::V6, 7_600_000),
101+
(ConsensusVersion::V7, 8_365_000),
102+
(ConsensusVersion::V8, 9_173_000),
103+
(ConsensusVersion::V9, 9_800_000),
104+
(ConsensusVersion::V10, 10_525_000),
105+
(ConsensusVersion::V11, 11_952_000),
106+
```
107+
Obviously you don't have time to wait for 10 million blocks to access a newer feature like program upgradability, so `leo devnet` provides a way to manually set the consensus heights via the `--consensus-heights` flag:
108+
```bash
109+
leo devnet --snarkos <SNARKOS> --snarkos-features test_network --consensus-heights 0,1,2,3,4,5,6,7,8,9,10
110+
```
111+
Note that if you want to access the latest features, the number of comma-separated arguments you pass to this flag must be exactly equal to the latest consensus version.
112+
90113
Each time you stop and restart the chain, the prior state and history will be saved. You can clear any prior history by passing the `--clear-storage` flag:
91114
```bash
92-
leo devnet --snarkos <SNARKOS> --clear-storage
115+
leo devnet --snarkos <SNARKOS> --snarkos-features test_network --clear-storage
93116
```
94117
Clearing the ledger history may be useful if you wish to redeploy your program without changing the name. However, this will erase all transaction history and start a new instance of the Aleo blockchain from genesis.
95118

96-
97-
98119
## Deploying and Executing
99120

100121
When deploying or executing programs on a local devnet, make sure that endpoint is set to `http://localhost:3030` rather than any external API endpoints. You can do this either by manually setting the `ENDPOINT` environment variable, by passing the `--endpoint http://localhost:3030` flag in the CLI, or by setting the `ENDPOINT` variable in a `.env` file within the root directory of your Leo project.

0 commit comments

Comments
 (0)