You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/bindings.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,5 +40,5 @@ fn main(num: Int) -> Int {
40
40
This should be used sparingly, since it can come with either a performance hit, an increase in compiled output size, or both.
41
41
42
42
:::tip
43
-
The compiler will automatically inline `let` bindings if they are only referenced a single time. You almost never need to explicitly mark them as `inline`.
43
+
The compiler will automatically inline `let` bindings if they are only referenced a single time.
Calculates the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash of the bytes provided. This hash is calculated at runtime.
32
+
Calculates the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hash of the bytes provided. While this hashes the value at runtime, there is also a `sha256_inline` builtin that allows you to hash a constant value at compile time and insert the result into the program. Its use is discouraged (because hard coding 32 bytes is costly in CLVM) unless you have a very large constant value that you want to avoid hashing multiple times.
27
33
28
34
For example:
29
35
30
36
```rue
31
37
let hash = sha256("hello");
32
38
```
33
39
34
-
### `sha256_inline`
35
-
36
-
The same as `sha256`, except the value will be calculated at compile time and inserted into the program. In some rare scenario, this may be an important optimization to avoid hashing the same large constant value many times. However, in most cases, you should just use the regular `sha256` function.
37
-
38
-
For example:
40
+
There's also a variant with the spread syntax that allows you to hash a list of arguments that is computed at runtime (rather than a static list):
39
41
40
42
```rue
41
-
let hash = sha256_inline("hello");
43
+
let hash = sha256(...list);
42
44
```
43
45
46
+
Note that this prevents the compiler from being able to fold the arguments into a constant at compile time. However, it saves CLVM cost by not having to recursively concatenate the arguments.
47
+
44
48
### `keccak256`
45
49
46
-
Calculates the [Keccak-256](https://en.wikipedia.org/wiki/SHA-3) hash of the bytes provided. This hash is calculated at runtime.
50
+
Calculates the [Keccak-256](https://en.wikipedia.org/wiki/SHA-3) hash of the bytes provided. While this hashes the value at runtime, there is also a `keccak256_inline` builtin that allows you to hash a constant value at compile time and insert the result into the program. Its use is discouraged (because hard coding 32 bytes is costly in CLVM) unless you have a very large constant value that you want to avoid hashing multiple times.
47
51
48
52
For example:
49
53
50
54
```rue
51
55
let hash = keccak256("hello");
52
56
```
53
57
54
-
### `keccak256_inline`
55
-
56
-
The same as `keccak256`, except the value will be calculated at compile time and inserted into the program. In some rare scenario, this may be an important optimization to avoid hashing the same large constant value many times. However, in most cases, you should just use the regular `keccak256` function.
57
-
58
-
For example:
58
+
There's also a variant with the spread syntax that allows you to hash a list of arguments that is computed at runtime (rather than a static list):
59
59
60
60
```rue
61
-
let hash = keccak256_inline("hello");
61
+
let hash = keccak256(...list);
62
62
```
63
63
64
+
Note that this prevents the compiler from being able to fold the arguments into a constant at compile time. However, it saves CLVM cost by not having to recursively concatenate the arguments.
65
+
64
66
### `coinid`
65
67
66
68
A builtin CLVM opcode for hashing the parent coin id, puzzle hash, and amount of a coin into its coin id. See the [Security](/docs/security.md#untrusted-values) section for an explanation of why this opcode exists. Essentially, it adds runtime checks to make sure that the length of the parent coin id and puzzle hash are 32.
@@ -83,18 +85,12 @@ substr("hello", 1, 4)
83
85
84
86
This would return `"ell"`
85
87
86
-
### `substr_start`
87
-
88
-
Returns a substring of a byte value, given a starting index. Note that if the index is out of bounds, this will raise an error.
89
-
90
-
For example:
88
+
The end index is optional, and defaults to the length of the string:
91
89
92
90
```rue
93
-
substr_start("hello", 1)
91
+
substr("hello", 1)
94
92
```
95
93
96
-
This would return `"ello"`
97
-
98
94
### `g1_map`
99
95
100
96
Hashes the data to a BLS public key, with sha256 and ExpandMsgXmd.
@@ -105,14 +101,10 @@ For example:
105
101
g1_map("hello")
106
102
```
107
103
108
-
### `g1_map_dist`
109
-
110
-
Hashes the data to a BLS public key, with sha256 and ExpandMsgXmd. This function allows you to add a DST.
111
-
112
-
For example:
104
+
You can also specify an optional DST parameter:
113
105
114
106
```rue
115
-
g1_map_dst("hello", "dst")
107
+
g1_map("hello", "dst")
116
108
```
117
109
118
110
### `g2_map`
@@ -125,14 +117,10 @@ For example:
125
117
g2_map("hello")
126
118
```
127
119
128
-
### `g2_map_dst`
129
-
130
-
Hashes the data to a BLS signature, with sha256 and ExpandMsgXmd. This function allows you to add a DST.
131
-
132
-
For example:
120
+
You can also specify an optional DST parameter:
133
121
134
122
```rue
135
-
g2_map_dst("hello", "dst")
123
+
g2_map("hello", "dst")
136
124
```
137
125
138
126
### `pubkey_for_exp`
@@ -155,6 +143,31 @@ For example:
155
143
modpow(5, 50, 45)
156
144
```
157
145
146
+
### `divmod`
147
+
148
+
Computes both `numerator / denominator` and `numerator % denominator` and returns a pair of the results.
149
+
150
+
For example:
151
+
152
+
```rue
153
+
let (quotient, remainder) = divmod(50, 3);
154
+
```
155
+
156
+
## Operator Optimizations
157
+
158
+
Similarly to [sha256](#sha256) and [keccak256](#keccak256), the following builtin functions are provided to optimize the use of operators on lists of arguments:
159
+
160
+
1.`concat` (the `+` operator on bytes)
161
+
2.`sum` (the `+` operator on integers)
162
+
3.`difference` (the `-` operator on integers)
163
+
4.`product` (the `*` operator on integers)
164
+
5.`any` (the `|` operator on booleans)
165
+
6.`all` (the `&` operator on booleans)
166
+
7.`g1_sum` (the `+` operator on BLS public keys)
167
+
8.`g1_difference` (the `-` operator on BLS public keys)
168
+
9.`g2_sum` (the `+` operator on BLS signatures)
169
+
10.`g2_difference` (the `-` operator on BLS signatures)
170
+
158
171
## Verifications
159
172
160
173
The following CLVM operators return `nil` on success, and raise if they fail:
@@ -164,16 +177,12 @@ The following CLVM operators return `nil` on success, and raise if they fail:
164
177
3.`secp256k1_verify`
165
178
4.`secp256r1_verify`
166
179
167
-
As such, they would be difficult to use if they were implemented as builtin functions. Instead, Rue provides verification **statements**, which can be called similarly to functions but can't directly be used as an expression. You can think of these like specialized `assert` statements, since they will raise if the verification fails, and continue on if not.
180
+
You can think of these like specialized `assert` statements, since they will raise if the verification fails, and continue on if not. Rue provides these as builtin functions that can be called as a statement.
168
181
169
182
These are automatically optimized such that ordinary references to `nil` after the verification statements in the block are replaced with the verification expression (since it returns `nil` anyways). If there aren't any possible substitutions, the rest of the block will be wrapped in a cons pair, with the verifications happening first, and then unwrapped back into the expression value of the block. And finally, if multiple verifications need to be done in a group, the `all` operator will be used, since it will always return `nil` as well.
170
183
171
184
All of this is to say that the compiler will pick the most efficient way to insert these verifications into your program without disrupting its behavior at runtime.
172
185
173
-
:::note
174
-
It's not currently possible to dynamically construct the arguments to these verifications from a list at runtime. This is a limitation that seems to exist in Chialisp as well. This may be possible in the future.
175
-
:::
176
-
177
186
### `bls_pairing_identity`
178
187
179
188
Accepts a flattened sequence of public key and signature pairs, and verifies that the pairing of all pairs is the identity. Otherwise, this will raise.
@@ -193,6 +202,8 @@ bls_pairing_identity(
193
202
);
194
203
```
195
204
205
+
You can use the spread syntax to verify a list of pairs.
206
+
196
207
### `bls_verify`
197
208
198
209
Accepts a signature followed by a flattened sequence of public key and message pairs, and verifies that the aggregate signature is valid. Otherwise, this will raise.
@@ -207,6 +218,8 @@ bls_verify(
207
218
);
208
219
```
209
220
221
+
You can use the spread syntax to verify a list of pairs.
222
+
210
223
### `secp256k1_verify`
211
224
212
225
Verifies a signature that uses the secp256r1 curve against its message hash and signature.
Copy file name to clipboardExpand all lines: docs/functions.md
+60-18Lines changed: 60 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,9 +20,9 @@ And they can be called by other functions:
20
20
add(42, 34)
21
21
```
22
22
23
-
## Main
23
+
## Entrypoints
24
24
25
-
The `main` function is special, in that it's the entrypoint to every compiled program. It's used to determine which symbols are used in the program, and its body is compiled verbatim (along with any functions and constants it depends on) rather than being called.
25
+
The `main` function is the primary entrypoint to your program. It's used to determine which symbols are used and its body is compiled verbatim (along with any functions and constants it depends on) rather than being called.
26
26
27
27
For example, the following program:
28
28
@@ -34,6 +34,32 @@ fn main() -> Int {
34
34
35
35
Will compile to the CLVM `(q . 42)`.
36
36
37
+
### Test Functions
38
+
39
+
Another form of entrypoint (which behaves like `main`) is a test function.
40
+
41
+
This lets you write unit tests directly in your Rue code:
42
+
43
+
```rue
44
+
test fn verify() {
45
+
assert main() == 42;
46
+
}
47
+
```
48
+
49
+
When you run `rue test`, all of the tests in the file will be executed. If they raise an error, the line number of the assertion will be included in the error message.
50
+
51
+
### Exported Functions
52
+
53
+
The last kind of entrypoint is any function that is marked with `export`:
54
+
55
+
```rue
56
+
export fn puzzle() -> List<Condition> {
57
+
nil
58
+
}
59
+
```
60
+
61
+
If you run `rue build -e puzzle`, it will build this export as if it were the main function. This lets you include multiple programs inside of a single file.
62
+
37
63
## Inline Functions
38
64
39
65
You can mark a function as `inline`. This will change the behavior of the compiler to insert the function body everywhere it's referenced, substituting each of the parameters with the corresponding arguments in the function call, instead of defining the function separately and calling it.
@@ -55,17 +81,31 @@ This should be used sparingly, since it can come with either a performance hit,
55
81
A recursive function cannot be marked as `inline`.
56
82
57
83
:::tip
58
-
The compiler will automatically inline functions if they are referenced a single time and their parameters are referenced a single time. You almost never need to explicitly mark them as `inline`.
84
+
The compiler will automatically inline functions if they are referenced a single time and their parameters are referenced a single time.
59
85
:::
60
86
61
-
## Spread Syntax
87
+
## Extern Functions
88
+
89
+
Function parameters are optimized into an efficient binary tree structure, as long as the function is neither an entrypoint, nor used as a closure. This binary tree optimization is not allowed for those because it's an implementation detail of the compiler and hard to rely on as a public interface. For example, if you accept a function as an argument, its arguments must be in the expected layout.
90
+
91
+
The compiler will pick the optimized parameter layout automatically when it can, but you can opt out of this with the `extern` keyword:
92
+
93
+
```rue
94
+
extern fn add(a: Int, b: Int) -> Int {
95
+
a + b
96
+
}
97
+
```
98
+
99
+
Just keep in mind that this can increase the size of the generated program, as well as its runtime CLVM cost.
62
100
63
-
Typically, there is a fixed number of arguments in a function. However, if you want to allow a variable number of arguments, or extend the argument list with another type, you can use the spread operator.
101
+
### Spread Syntax
64
102
65
-
For example, the following function accepts any number of arguments:
103
+
Although it's not very useful for optimized functions (since the parameter layout is managed by the compiler anyways), you can remove the nil terminator from the parameter list in an extern function by using the spread operator. You can think of this as binding a parameter to the "rest" of the parameters in CLVM.
104
+
105
+
For example:
66
106
67
107
```rue
68
-
fn sum(...numbers: List<Int>) -> Int {
108
+
extern fn sum(...numbers: List<Int>) -> Int {
69
109
if numbers is nil {
70
110
0
71
111
} else {
@@ -74,18 +114,16 @@ fn sum(...numbers: List<Int>) -> Int {
74
114
}
75
115
```
76
116
77
-
And can be called as such:
117
+
However, Rue does not allow you to specify an arbitrary number of arguments, and will force you to use the spread syntax on a list manually.
78
118
79
-
```rue
80
-
let zero = sum();
81
-
let added = sum(42, 34);
119
+
So, this can be called like this:
82
120
83
-
// You can spread arguments too
121
+
```rue
84
122
let list = [1, 2, 3, 4, 5];
85
-
let total = sum(42, ...list);
123
+
let total = sum(...list);
86
124
```
87
125
88
-
It's also possible to use the spread operator with non-list types, which allows for compatibility with arbitrary functions:
126
+
It's also possible to use the spread syntax with non-list types, which allows for flexibility with external functions:
let message = tree_hash(solution.conditions as Any);
146
+
let message = tree_hash(solution.conditions);
109
147
let agg_sig = AggSigMe {
110
148
public_key: solution.public_key,
111
-
message: message,
149
+
message,
112
150
};
113
151
[agg_sig, ...solution.conditions]
114
152
}
115
153
```
116
154
117
-
## Lambdas
155
+
## Closures
118
156
119
157
Because functions can be passed around as values, it's sometimes useful to be able to write a function inline.
120
158
@@ -125,7 +163,11 @@ let adder = fn(a: Int, b: Int) => a + b;
125
163
let sum = adder(42, 34);
126
164
```
127
165
128
-
### Closures
166
+
:::warning
167
+
Creating and calling function values limits the compiler's ability to optimize the generated bytecode, since they are forced to be compatible with external programs. You should use lambdas and closures sparingly, if at all.
168
+
:::
169
+
170
+
### Captures
129
171
130
172
Lambdas can automatically capture values that are defined in their parent scope, much like how functions in general capture other functions or constants that are defined externally.
0 commit comments