Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Commit

Permalink
feat(huffc): enable specifying evm version (#281)
Browse files Browse the repository at this point in the history
* feat: make literal gen reusable and default to push0

* feat: update tests to reflect push0 codegen change

* feat: make changes, cleanup doc tests

* feat: evm version scaffold

* feat: user can specify whether to implement push0

* chore(huffc): bump packages version

* fix: update tests to use evm_version

* feat: add paris codegen tests

* fix: clippy

* fix: update benchmarks

clean

* fix: rename is_shanghai to has_push0

* revert: accidental removal of help

* chore: update lock

* chore(lint): clippy + fmt

---------

Co-authored-by: Philogy <[email protected]>
Co-authored-by: Maddiaa0 <[email protected]>
  • Loading branch information
3 people committed Jul 8, 2023
1 parent 10aa9d0 commit a7403c9
Show file tree
Hide file tree
Showing 44 changed files with 661 additions and 358 deletions.
400 changes: 217 additions & 183 deletions Cargo.lock

Large diffs are not rendered by default.

45 changes: 21 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

> `huff-rs` is a [Huff](https://github.com/huff-language) compiler built in rust.

## What is a Huff?

Huff is a low-level programming language designed for developing highly optimized smart contracts that run on the Ethereum Virtual Machine (EVM). Huff does not hide the inner workings of the EVM. Instead, Huff exposes its programming stack to the developer for manual manipulation.
Expand All @@ -17,7 +16,6 @@ While EVM experts can use Huff to write highly-efficient smart contracts for use

To dive deeper into [Huff](https://github.com/huff-language), visit the [Official Huff Docs](https://huff.sh)(also available on [github](https://github.com/huff-language/huff-docs)).


## Installation

_Something not working? Send a message in [discord](https://discord.huff.sh)._
Expand All @@ -33,14 +31,16 @@ To avoid redirecting the script directly into bash, download and run the [huffup
To install the Huff compiler, simply run `huffup`.

If you have the old [huffc (TypeScript version)](https://github.com/huff-language/huffc) npm package installed globally, you can remove it with:

```bash
sudo yarn global remove huffc
```

To make sure you are running the rust version, you can run `huffc --version` and it should respond with `huff_cli <version>`. If it responds with `2.0.0` that means you are running the Typescript version.
To make sure you are running the rust version, you can run `huffc --version` and it should respond with `huff_cli <version>`. If it responds with `2.0.0` that means you are running the Typescript version.

```bash
$ huffc --version
huff_cli 0.3.1
huff_cli 0.3.2
```

**Alternatively**
Expand All @@ -59,43 +59,41 @@ OR
cargo install --git https://raw.githubusercontent.com/huff-language/huff-rs --locked huff_cli
```


## How Fast?

**Compilation Benchmarks**

| Compiler | Cold (No Cache) | Light Cache | Deep Cache | Full Cache |
| -------------------------------- | --------------- | ----------- | ---------- | ---------- |
| [huff-language/huff-rs][huff-rs] | XXXms | XXXms | XXXms | XXXms |
| [huff-language/huffc][huffc] | XXXms | XXXms | XXXms | XXXms |
| [huff-language/huff-rs][huff-rs] | XXXms | XXXms | XXXms | XXXms |
| [huff-language/huffc][huffc] | XXXms | XXXms | XXXms | XXXms |

_Note: Compilation benchmarks were performed on [huff-examples erc20](https://github.com/huff-language/huff-examples/tree/main/erc20/contracts/ERC20.huff)._


## Architecture

![Huff Compiler Architecture](./assets/huffc.png)


## Modules
* [huff_core](./huff_core): The core module to huff-rs. Resolves source file paths, executes compilation, and exports artifacts.
* [huff_cli](./huff_cli): The command line interface for the Huff compiler.
* [huff_js](./huff_js): A wasm compatible interface to the Huff compiler for JavaScript bindings.
* [huff_lexer](./huff_lexer): Takes in the source of a `.huff` file and generates a vector of `Token`s.
* [huff_parser](./huff_parser): Crafts a `Contract` AST from the vector of `Token`s generated by [huff_lexer](./huff_lexer).
* [huff_codegen](./huff_codegen): EVM Bytecode generation module that accepts an AST generated by [huff_parser](./huff_parser).
* [huff_utils](./huff_utils): Various utilities and types used by all modules.
* [huffup](./huffup): Update or revert to a specific huff-rs branch with ease. (Forked from [foundry](https://github.com/foundry-rs/foundry))

- [huff_core](./huff_core): The core module to huff-rs. Resolves source file paths, executes compilation, and exports artifacts.
- [huff_cli](./huff_cli): The command line interface for the Huff compiler.
- [huff_js](./huff_js): A wasm compatible interface to the Huff compiler for JavaScript bindings.
- [huff_lexer](./huff_lexer): Takes in the source of a `.huff` file and generates a vector of `Token`s.
- [huff_parser](./huff_parser): Crafts a `Contract` AST from the vector of `Token`s generated by [huff_lexer](./huff_lexer).
- [huff_codegen](./huff_codegen): EVM Bytecode generation module that accepts an AST generated by [huff_parser](./huff_parser).
- [huff_utils](./huff_utils): Various utilities and types used by all modules.
- [huffup](./huffup): Update or revert to a specific huff-rs branch with ease. (Forked from [foundry](https://github.com/foundry-rs/foundry))

## Contributing

All contributions are welcome! We want to make contributing to this project as easy and transparent as possible, whether it's:
- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- Becoming a maintainer

- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- Becoming a maintainer

We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/huff-language/huff-rs/issues/new); it's that easy!

Expand Down Expand Up @@ -141,7 +139,6 @@ When the PR checklist isn't complete, it is **highly** recommended to make it a

For breaking changes: make sure to edit the [excalidraw asset](https://excalidraw.com/#json=9YvTZp-rY9NOQnX9TC8Dz,sVM8vpgvQqGiXNXrBNshTg) and export the file to [./assets/huffc.excalidraw](./assets/huffc.excalidraw) along with an image to [./assets/huffc.png](./assets/huffc.png).


## Safety

> **Warning**
Expand All @@ -150,13 +147,13 @@ For breaking changes: make sure to edit the [excalidraw asset](https://excalidra
> Expect rapid iteration and **use at your own risk**.
>
> This code is **not designed for safety**.
>
> - There are untested invariants in the code that may break.
> - **You can easily shoot yourself in the foot if you're not careful.**
> - You should thoroughly read the documentation and examples.
>
> We **do not give any warranties** and **will not be liable for any loss** incurred through any use of this codebase.

## Acknowledgements

The original [Huff Language](https://github.com/huff-language) compiler: [`huffc`](https://github.com/huff-language/huffc).
Expand Down
2 changes: 1 addition & 1 deletion huff_cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "huff_cli"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
authors = ["refcell", "clabby", "exp.table", "maddiaa"]
readme = "README.md"
Expand Down
90 changes: 68 additions & 22 deletions huff_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,87 @@ The `huffc` CLI is written using [clap's](https://docs.rs/clap) [derive feature]
## huffc

```
huffc 0.3.1
huffc 0.3.2
Huff Language Compiler built in Pure Rust.
USAGE:
huffc [OPTIONS] [--] [PATH]
huffc [OPTIONS] [PATH] [SUBCOMMAND]
ARGS:
<PATH> The contract(s) to compile
OPTIONS:
-a, --artifacts Whether to generate artifacts or not
-b, --bytecode Generate and log bytecode
-d, --output-directory <OUTPUTDIR> The output directory [default: ./artifacts]
-g, --interface Generate solidity interface for a Huff artifact
-h, --help Print help information
-i, --inputs <INPUTS>... The input constructor arguments
-n, --interactive Interactively input the constructor args
-o, --output <OUTPUT> The output file path
-p, --print Prints out to the terminal
-r, --bin-runtime Generate and log runtime bytecode
-s, --source-path <SOURCE> The contracts source path [default: ./contracts]
-v, --verbose Verbose output
-V, --version Print version information
-z, --optimize Optimize compilation [WIP]
-a, --artifacts
Whether to generate artifacts or not
-b, --bytecode
Generate and log bytecode
-c, --constants <CONSTANTS>...
Override / set constants for the compilation environment
-d, --output-directory <OUTPUTDIR>
The output directory [default: ./artifacts]
-e, --evm-version <EVM_VERSION>
Set the EVM version
-g, --interface [<INTERFACE>...]
Generate solidity interface for a Huff artifact
-h, --help
Print help information
-i, --inputs <INPUTS>...
The input constructor arguments
-l, --label-indices
Prints out the jump label PC indices for the specified contract
-m, --alt-main <ALTERNATIVE_MAIN>
Compile a specific macro
-n, --interactive
Interactively input the constructor args
-o, --output <OUTPUT>
The output file path
-p, --print
Prints out to the terminal
-r, --bin-runtime
Generate and log runtime bytecode
-s, --source-path <SOURCE>
The contracts source path [default: ./contracts]
-t, --alt-constructor <ALTERNATIVE_CONSTRUCTOR>
Compile a specific constructor macro
-v, --verbose
Verbose output
-V, --version
Print version information
-z, --optimize
Optimize compilation [WIP]
```

_NOTE: To generate the above output, run: `huffc --help`_


## Usage

To run `huffc` from the command line, you can simply run:

```bash
huffc --help
```

By default, huffc will attempt to compile all contracts in the `contracts` directory. If there is no `contracts` directory present, the following will spit out an error like so:

```bash,color=red
~ huffc
Expand Down Expand Up @@ -90,18 +134,19 @@ huffc -o ./artifact.json ./huff-examples/erc20/contracts/ERC20.huff
```

**NOTE**: The following will _not_ compile since multiple artifacts cannot be output to the same artifact json file.

```bash
huffc -o ./artifact.json ./contracts/
```


#### Entering Constructor Arguments

`huffc` supports passing in constructor arguments to the contract. This is done by passing in the `--interactive` (shorthand: `-n`) flag or passing the `--inputs` (shorthand: `-i`) flag.

and passing in the arguments as a comma separated list.
and passing in the arguments as a comma separated list.

For example, to compile a contract (let's call it `example.huff`) with the following constructor definition:

```huff
#define macro CONSTRUCTOR(uint256, address) = takes(0) returns (0) {
0x04 calldataload
Expand All @@ -119,14 +164,14 @@ $ huffc -b -n ./contracts/example.huff
[INTERACTIVE] Enter a uint256 for constructor param: 100
[INTERACTIVE] Enter a address for constructor param: 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef

33600.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
335f.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
```

Alternatively, you can enter the arguments as a comma separated list by using the `-i` or `--inputs` flag like so:

```bash
$ huffc -b -i 100, 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef ./contracts/example.huff
33600.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
335f0.....f30000000000000000000000000000000000000000000000000000000000000064000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
```

#### Other Options
Expand All @@ -136,15 +181,16 @@ $ huffc -b -i 100, 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef ./contracts/exampl
- `-z` or `--optimize`: Optimizes the contract compilation - a work in progress.
- `-g` or `--interface`: Generates a solidity interface for the contract.


## Building huffc from source

To run `huffc` from the command line, you can use the following command:

```bash
cargo run --bin huffc
```

To pass arguments into the `huffc` binary, simply pass them in after a `--` flag. For example, to get the `huffc` version (a `-V` flag), you can run:

```bash
cargo run --bin huffc -- -V
```
Expand Down
12 changes: 11 additions & 1 deletion huff_cli/src/huffc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ use huff_utils::{
file_provider::FileSystemFileProvider,
prelude::{
export_interfaces, gen_sol_interfaces, str_to_bytes32, unpack_files, AstSpan, BytecodeRes,
CodegenError, CodegenErrorKind, CompilerError, FileSource, Literal, OutputLocation, Span,
CodegenError, CodegenErrorKind, CompilerError, EVMVersion, FileSource, Literal,
OutputLocation, Span,
},
};
use isatty::stdout_isatty;
Expand Down Expand Up @@ -99,6 +100,10 @@ struct Huff {
#[clap(short = 't', long = "alt-constructor")]
alternative_constructor: Option<String>,

/// Set the EVM version
#[clap(short = 'e', long = "evm-version")]
evm_version: Option<String>,

/// Test subcommand
#[clap(subcommand)]
test: Option<TestCommands>,
Expand Down Expand Up @@ -185,6 +190,9 @@ fn main() {
.collect()
});

// Parse the EVM version
let evm_version = EVMVersion::from(cli.evm_version);

let mut use_cache = true;
if cli.interactive {
// Don't accept configured inputs
Expand All @@ -204,6 +212,7 @@ fn main() {
};

let compiler: Compiler = Compiler {
evm_version: &evm_version,
sources: Arc::clone(&sources),
output,
alternative_main: cli.alternative_main.clone(),
Expand Down Expand Up @@ -244,6 +253,7 @@ fn main() {

// Recurse through the macro and generate bytecode
let bytecode_res: BytecodeRes = Codegen::macro_to_bytecode(
&evm_version,
macro_def,
contract,
&mut vec![macro_def],
Expand Down
2 changes: 1 addition & 1 deletion huff_codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "huff_codegen"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
authors = ["refcell", "clabby", "exp.table", "maddiaa"]
readme = "README.md"
Expand Down
14 changes: 7 additions & 7 deletions huff_codegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ assert!(cg.ast.is_none());
assert!(cg.artifact.is_none());

// ERC20 Bytecode
let main_bytecode = "60003560E01c8063a9059cbb1461004857806340c10f19146100de57806370a082311461014e57806318160ddd1461016b578063095ea7b314610177578063dd62ed3e1461018e575b600435336024358160016000526000602001526040600020548082116100d8578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020556000527fDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF60206000a3600160005260206000f35b60006000fd5b60005433146100ed5760006000fd5b600435600060243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002556000527fDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF60206000a35b600435600160005260006020015260406000205460005260206000f35b60025460005260206000f35b602435600435336000526000602001526040600020555b60243560043560005260006020015260406000205460005260206000f3";
let constructor_bytecode = "33600055";
let main_bytecode = "5f3560e01c8063a9059cbb1461004757806340c10f19146100d757806370a082311461014157806318160ddd1461015c578063095ea7b314610166578063dd62ed3e1461017d575b600435336024358160016000526000602001526040600020548082116100d3578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa360015f5260205ff35b5f5ffd5b5f5433146100e3575f5ffd5b6004355f60243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa35b60043560016000526000602001526040600020545f5260205ff35b6002545f5260205ff35b602435600435336000526000602001526040600020555b6024356004356000526000602001526040600020545f5260205ff3";
let constructor_bytecode = "335f55";
let inputs = vec![];
let churn_res = cg.churn(Arc::new(FileSource::default()), inputs, main_bytecode, constructor_bytecode, false);

// Validate the output bytecode
assert_eq!(churn_res.unwrap().bytecode, "336000556101ac80600e3d393df360003560e01c8063a9059cbb1461004857806340c10f19146100de57806370a082311461014e57806318160ddd1461016b578063095ea7b314610177578063dd62ed3e1461018e575b600435336024358160016000526000602001526040600020548082116100d8578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020556000527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a3600160005260206000f35b60006000fd5b60005433146100ed5760006000fd5b600435600060243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002556000527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000a35b600435600160005260006020015260406000205460005260206000f35b60025460005260206000f35b602435600435336000526000602001526040600020555b60243560043560005260006020015260406000205460005260206000f3".to_lowercase());
assert_eq!(churn_res.unwrap().bytecode, "335f5561019980600d3d393df35f3560e01c8063a9059cbb1461004757806340c10f19146100d757806370a082311461014157806318160ddd1461015c578063095ea7b314610166578063dd62ed3e1461017d575b600435336024358160016000526000602001526040600020548082116100d3578190038260016000526000602001526040600020558281906001600052600060200152604060002054018360016000526000602001526040600020555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa360015f5260205ff35b5f5ffd5b5f5433146100e3575f5ffd5b6004355f60243582819060016000526000602001526040600020540183600160005260006020015260406000205580600254016002555f527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60205fa35b60043560016000526000602001526040600020545f5260205ff35b6002545f5260205ff35b602435600435336000526000602001526040600020555b6024356004356000526000602001526040600020545f5260205ff3".to_lowercase());

// Write the compile artifact out to a file
// cg.export("./output.json");
Expand Down Expand Up @@ -97,10 +97,10 @@ let contract = Contract {
};

// Generate the main bytecode
let main_bytecode: String = Codegen::generate_main_bytecode(&contract, None).unwrap();
let main_bytecode: String = Codegen::generate_main_bytecode(&EVMVersion::default(), &contract, None).unwrap();

// Validate the output bytecode
assert_eq!(main_bytecode, "60003560e01c");
assert_eq!(main_bytecode, "5f3560e01c");
```

Similarly, once you have a [Contract](../huff_utils/ast/struct.Contract.html) instance with a simple **CONSTRUCTOR** macro definition. You can generate the constructor/creation bytecode using the [generate_constructor_bytecode](struct.Codegen.html#method.generate_constructor_bytecode) function.
Expand Down Expand Up @@ -152,9 +152,9 @@ let contract = Contract {
};

// Generate the constructor bytecode
let (constructor_bytecode, has_custom_bootstrap): (String, bool) = Codegen::generate_constructor_bytecode(&contract, None).unwrap();
let (constructor_bytecode, has_custom_bootstrap): (String, bool) = Codegen::generate_constructor_bytecode(&EVMVersion::default(), &contract, None).unwrap();

// Validate the output bytecode
assert_eq!(constructor_bytecode, "60003560e01c");
assert_eq!(constructor_bytecode, "5f3560e01c");
assert_eq!(has_custom_bootstrap, false);
```
Loading

0 comments on commit a7403c9

Please sign in to comment.