-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: yul lagrange basis evaluation #442
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,3 +67,5 @@ cache | |
|
||
# any output files from generating public params | ||
output/ | ||
|
||
*.p.sol |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
crates/proof-of-sql/sol_src/base/LagrangeBasisEvaluation.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
contract LagrangeBasisEvaluation { | ||
function computeTruncatedLagrangeBasisSum(uint256 length0, bytes memory point0, uint256 numVars0, uint256 modulus0) | ||
public | ||
pure | ||
returns (uint256 result0) | ||
{ | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly { | ||
// START-YUL compute_truncated_lagrange_basis_sum | ||
function compute_truncated_lagrange_basis_sum(length, point, num_vars, modulus) -> result { | ||
let ONE := add(modulus, 1) | ||
// result := 0 // implicitly set by the EVM | ||
|
||
// Invariant that holds within the for loop: | ||
// 0 <= result <= modulus + 1 | ||
// This invariant reduces modulus operations. | ||
for {} num_vars {} { | ||
switch and(length, 1) | ||
case 0 { result := mulmod(result, sub(ONE, mod(mload(point), modulus)), modulus) } | ||
default { result := sub(ONE, mulmod(sub(ONE, result), mload(point), modulus)) } | ||
num_vars := sub(num_vars, 1) | ||
length := shr(1, length) | ||
point := add(point, 32) | ||
} | ||
switch length | ||
case 0 { result := mod(result, modulus) } | ||
default { result := 1 } | ||
} | ||
// END-YUL | ||
result0 := compute_truncated_lagrange_basis_sum(length0, add(point0, 32), numVars0, modulus0) | ||
} | ||
} | ||
|
||
uint256 private constant TEST_MODULUS = 10007; | ||
|
||
function testComputeTruncatedLagrangeBasisSumGivesCorrectValuesWith0Variables() public pure { | ||
bytes memory point = hex""; | ||
assert(computeTruncatedLagrangeBasisSum(1, point, 0, TEST_MODULUS) == 1); | ||
assert(computeTruncatedLagrangeBasisSum(0, point, 0, TEST_MODULUS) == 0); | ||
} | ||
|
||
function testComputeTruncatedLagrangeBasisSumGivesCorrectValuesWith1Variables() public pure { | ||
bytes memory point = hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000002"; | ||
assert(computeTruncatedLagrangeBasisSum(2, point, 1, TEST_MODULUS) == 1); | ||
assert(computeTruncatedLagrangeBasisSum(1, point, 1, TEST_MODULUS) == TEST_MODULUS - 1); | ||
assert(computeTruncatedLagrangeBasisSum(0, point, 1, TEST_MODULUS) == 0); | ||
} | ||
|
||
function testComputeTruncatedLagrangeBasisSumGivesCorrectValuesWith2Variables() public pure { | ||
bytes memory point = hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000002" | ||
hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000005"; | ||
assert(computeTruncatedLagrangeBasisSum(4, point, 2, TEST_MODULUS) == 1); | ||
assert(computeTruncatedLagrangeBasisSum(3, point, 2, TEST_MODULUS) == TEST_MODULUS - 9); | ||
assert(computeTruncatedLagrangeBasisSum(2, point, 2, TEST_MODULUS) == TEST_MODULUS - 4); | ||
assert(computeTruncatedLagrangeBasisSum(1, point, 2, TEST_MODULUS) == 4); | ||
assert(computeTruncatedLagrangeBasisSum(0, point, 2, TEST_MODULUS) == 0); | ||
} | ||
|
||
function testComputeTruncatedLagrangeBasisSumGivesCorrectValuesWith3Variables() public pure { | ||
bytes memory point = hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000002" | ||
hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000005" | ||
hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" hex"0000000000000007"; | ||
assert(computeTruncatedLagrangeBasisSum(8, point, 3, TEST_MODULUS) == 1); | ||
assert(computeTruncatedLagrangeBasisSum(7, point, 3, TEST_MODULUS) == TEST_MODULUS - 69); | ||
assert(computeTruncatedLagrangeBasisSum(6, point, 3, TEST_MODULUS) == TEST_MODULUS - 34); | ||
assert(computeTruncatedLagrangeBasisSum(5, point, 3, TEST_MODULUS) == 22); | ||
assert(computeTruncatedLagrangeBasisSum(4, point, 3, TEST_MODULUS) == TEST_MODULUS - 6); | ||
assert(computeTruncatedLagrangeBasisSum(3, point, 3, TEST_MODULUS) == 54); | ||
assert(computeTruncatedLagrangeBasisSum(2, point, 3, TEST_MODULUS) == 24); | ||
assert(computeTruncatedLagrangeBasisSum(1, point, 3, TEST_MODULUS) == TEST_MODULUS - 24); | ||
assert(computeTruncatedLagrangeBasisSum(0, point, 3, TEST_MODULUS) == 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
library TestScript { | ||
function testWeCanImportYulFromAnotherFile() public pure { | ||
bytes memory point0 = hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" | ||
hex"0000000000000002" hex"0000000000000000" hex"0000000000000000" hex"0000000000000000" | ||
hex"0000000000000005"; | ||
uint256 length0 = 1; | ||
uint256 numVars0 = 2; | ||
uint256 modulus0 = 10007; | ||
uint256 result0; | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly { | ||
// IMPORT-YUL ../base/LagrangeBasisEvaluation.sol:compute_truncated_lagrange_basis_sum | ||
// solhint-disable-next-line no-empty-blocks | ||
function compute_truncated_lagrange_basis_sum(length, point, num_vars, modulus) -> result {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this line isn't needed right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. It's needed for the sake of the linter, so it knows what the function signature is. |
||
// END-IMPORT-YUL | ||
result0 := compute_truncated_lagrange_basis_sum(length0, add(point0, 32), numVars0, modulus0) | ||
} | ||
assert(result0 == 4); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
//! This binary applies a preprocessing step to Solidity files that allows for importing Yul code from other files. | ||
|
||
use clap::Parser; | ||
use snafu::Snafu; | ||
use std::{ | ||
fs::{self, File}, | ||
io::{self, BufRead, BufWriter, Write}, | ||
path::Path, | ||
}; | ||
|
||
const IMPORT_YUL: &str = "// IMPORT-YUL"; | ||
const END_IMPORT_YUL: &str = "// END-IMPORT-YUL"; | ||
const START_YUL: &str = "// START-YUL"; | ||
const END_YUL: &str = "// END-YUL"; | ||
const IMPORTED_YUL: &str = "// IMPORTED-YUL"; | ||
const END_IMPORTED_YUL: &str = "// END-IMPORTED-YUL"; | ||
|
||
#[derive(Debug, Snafu)] | ||
enum Error { | ||
#[snafu(transparent)] | ||
Io { source: io::Error }, | ||
#[snafu(display("Ill-formed IMPORT-YUL statement at line {line}"))] | ||
IllFormedImportYul { line: usize }, | ||
#[snafu(display("Unmatched END-IMPORT-YUL at line {line}"))] | ||
UnmatchedEndImportYul { line: usize }, | ||
#[snafu(display("Unmatched IMPORT-YUL at line"))] | ||
UnmatchedImportYul, | ||
#[snafu(display("Function {function_name} not found in file {file_path}"))] | ||
FunctionNotFound { | ||
function_name: String, | ||
file_path: String, | ||
}, | ||
} | ||
|
||
/// A preprocessor for Solidity files to import Yul code | ||
/// | ||
/// This tool processes a given file or directory, replacing the import statements with the corresponding Yul code. | ||
/// | ||
/// # Usage | ||
/// | ||
/// The Yul code should be wrapped in `// START-YUL <function_name>` and `// END-YUL` comments in the source files. | ||
/// The import statement should be in the form `// IMPORT-YUL <file_path>:<function_name>` in the target Solidity files. | ||
/// | ||
/// # Example | ||
/// | ||
/// Given a Solidity file `example.psol` with the following content: | ||
/// | ||
/// ```solidity | ||
/// // IMPORT-YUL yul_code.sol:my_function | ||
/// // END-IMPORT-YUL | ||
/// ``` | ||
/// | ||
/// And a Yul file `yul_code.sol` with the following content: | ||
/// | ||
/// ```solidity | ||
/// // START-YUL my_function | ||
/// function my_function() -> result { | ||
/// // Yul code here | ||
/// } | ||
/// // END-YUL | ||
/// ``` | ||
/// | ||
/// Running the binary will produce an output file `example.p.sol` with the following content: | ||
/// | ||
/// ```solidity | ||
/// // IMPORTED-YUL yul_code.sol:my_function | ||
/// function my_function() -> result { | ||
/// // Yul code here | ||
/// } | ||
/// // END-IMPORTED-YUL | ||
/// ``` | ||
#[derive(Parser, Debug)] | ||
#[command(about, long_about)] | ||
struct Args { | ||
/// The path to the file or directory to process | ||
path: String, | ||
} | ||
|
||
fn main() -> Result<(), Error> { | ||
let args = Args::parse(); | ||
process_path(Path::new(&args.path))?; | ||
Ok(()) | ||
} | ||
|
||
fn process_path(path: &Path) -> Result<(), Error> { | ||
if path.is_dir() { | ||
for entry in fs::read_dir(path)? { | ||
process_path(&entry?.path())?; | ||
} | ||
} else if path.extension().and_then(|ext| ext.to_str()) == Some("psol") { | ||
process_file(path)?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn process_file(path: &Path) -> Result<(), Error> { | ||
let file = File::open(path)?; | ||
let reader = io::BufReader::new(file); | ||
let mut output_lines = Vec::new(); | ||
let mut inside_import = false; | ||
let mut import_file = String::new(); | ||
let mut function_name = String::new(); | ||
let base_path = path.parent().unwrap_or_else(|| Path::new("")); | ||
|
||
for (line_number, line) in reader.lines().enumerate() { | ||
let line = line?; | ||
if let Some(import_pos) = line.find(IMPORT_YUL) { | ||
let parts: Vec<&str> = line[import_pos + IMPORT_YUL.len()..].split(':').collect(); | ||
if parts.len() != 2 { | ||
return Err(Error::IllFormedImportYul { | ||
line: line_number + 1, | ||
}); | ||
} | ||
inside_import = true; | ||
import_file = parts[0].trim().to_string(); | ||
function_name = parts[1].trim().to_string(); | ||
} else if line.contains(END_IMPORT_YUL) { | ||
if !inside_import { | ||
return Err(Error::UnmatchedEndImportYul { | ||
line: line_number + 1, | ||
}); | ||
} | ||
let function_lines = extract_function(&base_path.join(&import_file), &function_name)?; | ||
output_lines.push(format!("{IMPORTED_YUL} {import_file}:{function_name}")); | ||
output_lines.extend(function_lines); | ||
output_lines.push(END_IMPORTED_YUL.to_string()); | ||
inside_import = false; | ||
} else if !inside_import { | ||
output_lines.push(line); | ||
} | ||
} | ||
|
||
if inside_import { | ||
return Err(Error::UnmatchedImportYul); | ||
} | ||
|
||
let file = File::create(path.with_extension("p.sol"))?; | ||
let mut writer = BufWriter::new(file); | ||
for line in output_lines { | ||
writeln!(writer, "{line}")?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn extract_function(file_path: &Path, function_name: &str) -> Result<Vec<String>, Error> { | ||
let file = File::open(file_path)?; | ||
let reader = io::BufReader::new(file); | ||
let mut function_lines = Vec::new(); | ||
let mut inside_function = false; | ||
|
||
for line in reader.lines() { | ||
let line = line?; | ||
if line.contains(&format!("{START_YUL} {function_name}")) { | ||
inside_function = true; | ||
} else if line.contains(END_YUL) { | ||
break; | ||
} else if inside_function { | ||
function_lines.push(line); | ||
} | ||
} | ||
|
||
if !inside_function { | ||
return Err(Error::FunctionNotFound { | ||
function_name: function_name.to_string(), | ||
file_path: file_path.display().to_string(), | ||
}); | ||
} | ||
|
||
Ok(function_lines) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps you want to add this to your solhint:
"no-inline-assembly": "off",
to avoid repeatedly using // solhint-disable-next-line rule