Skip to content

Commit d1eecef

Browse files
authored
Merge pull request #6 from xch-dev/new-release
New release
2 parents 53b0326 + 1e21645 commit d1eecef

File tree

7 files changed

+170
-71
lines changed

7 files changed

+170
-71
lines changed

docs/bindings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ fn main(num: Int) -> Int {
4040
This should be used sparingly, since it can come with either a performance hit, an increase in compiled output size, or both.
4141

4242
:::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.
4444
:::

docs/builtins.md

Lines changed: 56 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,55 @@ You can click on each of these types to go to their corresponding page and learn
1414
2. [**Bytes**](/docs/type-system/atoms.md#bytes)
1515
3. [**Bytes32**](/docs/type-system/atoms.md#bytes32)
1616
4. [**PublicKey**](/docs/type-system/atoms.md#publickey)
17-
5. [**Int**](/docs/type-system/atoms.md#int)
18-
6. [**Bool**](/docs/type-system/atoms.md#bool)
19-
7. [**Any**](/docs/type-system/pairs.md#any)
20-
8. [**List**](/docs/type-system/pairs.md#lists)
17+
5. [**Signature**](/docs/type-system/atoms.md#signature)
18+
6. [**K1PublicKey**](/docs/type-system/atoms.md#k1publickey)
19+
7. [**K1Signature**](/docs/type-system/atoms.md#k1signature)
20+
8. [**R1PublicKey**](/docs/type-system/atoms.md#r1publickey)
21+
9. [**R1Signature**](/docs/type-system/atoms.md#r1signature)
22+
10. [**Int**](/docs/type-system/atoms.md#int)
23+
11. [**Bool**](/docs/type-system/atoms.md#bool)
24+
12. [**Any**](/docs/type-system/pairs.md#any)
25+
13. [**List**](/docs/type-system/pairs.md#lists)
26+
14. [**AlternatingList**](/docs/type-system/pairs.md#alternating-lists)
2127

2228
## Functions
2329

2430
### `sha256`
2531

26-
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.
2733

2834
For example:
2935

3036
```rue
3137
let hash = sha256("hello");
3238
```
3339

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):
3941

4042
```rue
41-
let hash = sha256_inline("hello");
43+
let hash = sha256(...list);
4244
```
4345

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+
4448
### `keccak256`
4549

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.
4751

4852
For example:
4953

5054
```rue
5155
let hash = keccak256("hello");
5256
```
5357

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):
5959

6060
```rue
61-
let hash = keccak256_inline("hello");
61+
let hash = keccak256(...list);
6262
```
6363

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+
6466
### `coinid`
6567

6668
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)
8385

8486
This would return `"ell"`
8587

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:
9189

9290
```rue
93-
substr_start("hello", 1)
91+
substr("hello", 1)
9492
```
9593

96-
This would return `"ello"`
97-
9894
### `g1_map`
9995

10096
Hashes the data to a BLS public key, with sha256 and ExpandMsgXmd.
@@ -105,14 +101,10 @@ For example:
105101
g1_map("hello")
106102
```
107103

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:
113105

114106
```rue
115-
g1_map_dst("hello", "dst")
107+
g1_map("hello", "dst")
116108
```
117109

118110
### `g2_map`
@@ -125,14 +117,10 @@ For example:
125117
g2_map("hello")
126118
```
127119

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:
133121

134122
```rue
135-
g2_map_dst("hello", "dst")
123+
g2_map("hello", "dst")
136124
```
137125

138126
### `pubkey_for_exp`
@@ -155,6 +143,31 @@ For example:
155143
modpow(5, 50, 45)
156144
```
157145

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+
158171
## Verifications
159172

160173
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:
164177
3. `secp256k1_verify`
165178
4. `secp256r1_verify`
166179

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.
168181

169182
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.
170183

171184
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.
172185

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-
177186
### `bls_pairing_identity`
178187

179188
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(
193202
);
194203
```
195204

205+
You can use the spread syntax to verify a list of pairs.
206+
196207
### `bls_verify`
197208

198209
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(
207218
);
208219
```
209220

221+
You can use the spread syntax to verify a list of pairs.
222+
210223
### `secp256k1_verify`
211224

212225
Verifies a signature that uses the secp256r1 curve against its message hash and signature.

docs/constants.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ fn main(person_a: Bytes, person_b: Bytes) -> (Bytes, Bytes) {
3535
This should be used sparingly, since it can come with either a performance hit, an increase in compiled output size, or both.
3636

3737
:::tip
38-
The compiler will automatically inline constants if they are only referenced a single time. You almost never need to explicitly mark them as `inline`.
38+
The compiler will automatically inline constants if they are only referenced a single time.
3939
:::

docs/functions.md

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ And they can be called by other functions:
2020
add(42, 34)
2121
```
2222

23-
## Main
23+
## Entrypoints
2424

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.
2626

2727
For example, the following program:
2828

@@ -34,6 +34,32 @@ fn main() -> Int {
3434

3535
Will compile to the CLVM `(q . 42)`.
3636

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+
3763
## Inline Functions
3864

3965
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,
5581
A recursive function cannot be marked as `inline`.
5682

5783
:::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.
5985
:::
6086

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.
62100

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
64102

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:
66106

67107
```rue
68-
fn sum(...numbers: List<Int>) -> Int {
108+
extern fn sum(...numbers: List<Int>) -> Int {
69109
if numbers is nil {
70110
0
71111
} else {
@@ -74,18 +114,16 @@ fn sum(...numbers: List<Int>) -> Int {
74114
}
75115
```
76116

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.
78118

79-
```rue
80-
let zero = sum();
81-
let added = sum(42, 34);
119+
So, this can be called like this:
82120

83-
// You can spread arguments too
121+
```rue
84122
let list = [1, 2, 3, 4, 5];
85-
let total = sum(42, ...list);
123+
let total = sum(...list);
86124
```
87125

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:
89127

90128
```rue
91129
fn main(
@@ -105,16 +143,16 @@ struct Solution {
105143
}
106144
107145
fn main(...solution: Solution) -> List<Condition> {
108-
let message = tree_hash(solution.conditions as Any);
146+
let message = tree_hash(solution.conditions);
109147
let agg_sig = AggSigMe {
110148
public_key: solution.public_key,
111-
message: message,
149+
message,
112150
};
113151
[agg_sig, ...solution.conditions]
114152
}
115153
```
116154

117-
## Lambdas
155+
## Closures
118156

119157
Because functions can be passed around as values, it's sometimes useful to be able to write a function inline.
120158

@@ -125,7 +163,11 @@ let adder = fn(a: Int, b: Int) => a + b;
125163
let sum = adder(42, 34);
126164
```
127165

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
129171

130172
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.
131173

0 commit comments

Comments
 (0)