Skip to content

Commit

Permalink
Merge branch 'master' into ab/do-not-hoist-binaries
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Dec 2, 2024
2 parents 1ddba8a + e3a0914 commit 890b17e
Show file tree
Hide file tree
Showing 29 changed files with 942 additions and 541 deletions.
30 changes: 23 additions & 7 deletions .github/workflows/test-js-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,25 @@ jobs:
fail-fast: false
matrix:
project:
# Disabled as these are currently failing with many visibility errors
- { repo: AztecProtocol/aztec-nr, path: ./ }
- { repo: noir-lang/ec, path: ./ }
- { repo: noir-lang/eddsa, path: ./ }
- { repo: noir-lang/mimc, path: ./ }
- { repo: noir-lang/noir_sort, path: ./ }
- { repo: noir-lang/noir-edwards, path: ./ }
- { repo: noir-lang/noir-bignum, path: ./ }
- { repo: noir-lang/noir_bigcurve, path: ./ }
- { repo: noir-lang/noir_base64, path: ./ }
- { repo: noir-lang/noir_string_search, path: ./ }
- { repo: noir-lang/sparse_array, path: ./ }
- { repo: noir-lang/noir_rsa, path: ./lib }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/aztec-nr }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-contracts }
# Disabled as aztec-packages requires a setup-step in order to generate a `Nargo.toml`
#- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits }
- { repo: noir-lang/noir-edwards, path: ./, ref: 3188ea74fe3b059219a2ea87899589c266256d74 }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/parity-lib }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/private-kernel-lib }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/reset-kernel-lib }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/rollup-lib }
- { repo: AztecProtocol/aztec-packages, path: ./noir-projects/noir-protocol-circuits/crates/types }

name: Check external repo - ${{ matrix.project.repo }}
steps:
- name: Checkout
Expand Down Expand Up @@ -554,9 +567,12 @@ jobs:
# Github actions seems to not expand "**" in globs by default.
shopt -s globstar
sed -i '/^compiler_version/d' ./**/Nargo.toml
- name: Run nargo check
- name: Run nargo test
working-directory: ./test-repo/${{ matrix.project.path }}
run: nargo check
run: nargo test --silence-warnings
env:
NARGO_IGNORE_TEST_FAILURES_FROM_FOREIGN_CALLS: true

# This is a job which depends on all test jobs and reports the overall status.
# This allows us to add/remove test jobs without having to update the required workflows.
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_printable_type/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ pub enum PrintableValueDisplay<F> {

#[derive(Debug, Error)]
pub enum ForeignCallError {
#[error("No handler could be found for foreign call `{0}`")]
NoHandler(String),

#[error("Foreign call inputs needed for execution are missing")]
MissingForeignCallInputs,

Expand Down
2 changes: 1 addition & 1 deletion tooling/acvm_cli/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use clap::Args;

use crate::cli::fs::inputs::{read_bytecode_from_file, read_inputs_from_file};
use crate::errors::CliError;
use nargo::ops::{execute_program, DefaultForeignCallExecutor};
use nargo::{foreign_calls::DefaultForeignCallExecutor, ops::execute_program};

use super::fs::witness::{create_output_witness_string, save_witness_to_dir};

Expand Down
2 changes: 1 addition & 1 deletion tooling/debugger/src/foreign_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use acvm::{
pwg::ForeignCallWaitInfo,
AcirField, FieldElement,
};
use nargo::ops::{DefaultForeignCallExecutor, ForeignCallExecutor};
use nargo::foreign_calls::{DefaultForeignCallExecutor, ForeignCallExecutor};
use noirc_artifacts::debug::{DebugArtifact, DebugVars, StackFrame};
use noirc_errors::debug_info::{DebugFnId, DebugVarId};
use noirc_printable_type::ForeignCallError;
Expand Down
5 changes: 5 additions & 0 deletions tooling/lsp/src/requests/test_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ fn on_test_run_request_inner(
result: "fail".to_string(),
message: Some(message),
},
TestStatus::Skipped => NargoTestRunResult {
id: params.id.clone(),
result: "skipped".to_string(),
message: None,
},
TestStatus::CompileError(diag) => NargoTestRunResult {
id: params.id.clone(),
result: "error".to_string(),
Expand Down
176 changes: 176 additions & 0 deletions tooling/nargo/src/foreign_calls/mocker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use acvm::{
acir::brillig::{ForeignCallParam, ForeignCallResult},
pwg::ForeignCallWaitInfo,
AcirField,
};
use noirc_printable_type::{decode_string_value, ForeignCallError};
use serde::{Deserialize, Serialize};

use super::{ForeignCall, ForeignCallExecutor};

/// This struct represents an oracle mock. It can be used for testing programs that use oracles.
#[derive(Debug, PartialEq, Eq, Clone)]
struct MockedCall<F> {
/// The id of the mock, used to update or remove it
id: usize,
/// The oracle it's mocking
name: String,
/// Optionally match the parameters
params: Option<Vec<ForeignCallParam<F>>>,
/// The parameters with which the mock was last called
last_called_params: Option<Vec<ForeignCallParam<F>>>,
/// The result to return when this mock is called
result: ForeignCallResult<F>,
/// How many times should this mock be called before it is removed
times_left: Option<u64>,
}

impl<F> MockedCall<F> {
fn new(id: usize, name: String) -> Self {
Self {
id,
name,
params: None,
last_called_params: None,
result: ForeignCallResult { values: vec![] },
times_left: None,
}
}
}

impl<F: PartialEq> MockedCall<F> {
fn matches(&self, name: &str, params: &[ForeignCallParam<F>]) -> bool {
self.name == name && (self.params.is_none() || self.params.as_deref() == Some(params))
}
}

#[derive(Debug, Default)]
pub(crate) struct MockForeignCallExecutor<F> {
/// Mocks have unique ids used to identify them in Noir, allowing to update or remove them.
last_mock_id: usize,
/// The registered mocks
mocked_responses: Vec<MockedCall<F>>,
}

impl<F: AcirField> MockForeignCallExecutor<F> {
fn extract_mock_id(
foreign_call_inputs: &[ForeignCallParam<F>],
) -> Result<(usize, &[ForeignCallParam<F>]), ForeignCallError> {
let (id, params) =
foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?;
let id =
usize::try_from(id.unwrap_field().try_to_u64().expect("value does not fit into u64"))
.expect("value does not fit into usize");
Ok((id, params))
}

fn find_mock_by_id(&self, id: usize) -> Option<&MockedCall<F>> {
self.mocked_responses.iter().find(|response| response.id == id)
}

fn find_mock_by_id_mut(&mut self, id: usize) -> Option<&mut MockedCall<F>> {
self.mocked_responses.iter_mut().find(|response| response.id == id)
}

fn parse_string(param: &ForeignCallParam<F>) -> String {
let fields: Vec<_> = param.fields().to_vec();
decode_string_value(&fields)
}
}

impl<F: AcirField + Serialize + for<'a> Deserialize<'a>> ForeignCallExecutor<F>
for MockForeignCallExecutor<F>
{
fn execute(
&mut self,
foreign_call: &ForeignCallWaitInfo<F>,
) -> Result<ForeignCallResult<F>, ForeignCallError> {
let foreign_call_name = foreign_call.function.as_str();
match ForeignCall::lookup(foreign_call_name) {
Some(ForeignCall::CreateMock) => {
let mock_oracle_name = Self::parse_string(&foreign_call.inputs[0]);
assert!(ForeignCall::lookup(&mock_oracle_name).is_none());
let id = self.last_mock_id;
self.mocked_responses.push(MockedCall::new(id, mock_oracle_name));
self.last_mock_id += 1;

Ok(F::from(id).into())
}
Some(ForeignCall::SetMockParams) => {
let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?;
self.find_mock_by_id_mut(id)
.unwrap_or_else(|| panic!("Unknown mock id {}", id))
.params = Some(params.to_vec());

Ok(ForeignCallResult::default())
}
Some(ForeignCall::GetMockLastParams) => {
let (id, _) = Self::extract_mock_id(&foreign_call.inputs)?;
let mock =
self.find_mock_by_id(id).unwrap_or_else(|| panic!("Unknown mock id {}", id));

let last_called_params = mock
.last_called_params
.clone()
.unwrap_or_else(|| panic!("Mock {} was never called", mock.name));

Ok(last_called_params.into())
}
Some(ForeignCall::SetMockReturns) => {
let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?;
self.find_mock_by_id_mut(id)
.unwrap_or_else(|| panic!("Unknown mock id {}", id))
.result = ForeignCallResult { values: params.to_vec() };

Ok(ForeignCallResult::default())
}
Some(ForeignCall::SetMockTimes) => {
let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?;
let times =
params[0].unwrap_field().try_to_u64().expect("Invalid bit size of times");

self.find_mock_by_id_mut(id)
.unwrap_or_else(|| panic!("Unknown mock id {}", id))
.times_left = Some(times);

Ok(ForeignCallResult::default())
}
Some(ForeignCall::ClearMock) => {
let (id, _) = Self::extract_mock_id(&foreign_call.inputs)?;
self.mocked_responses.retain(|response| response.id != id);
Ok(ForeignCallResult::default())
}
_ => {
let mock_response_position = self
.mocked_responses
.iter()
.position(|response| response.matches(foreign_call_name, &foreign_call.inputs));

if let Some(response_position) = mock_response_position {
// If the program has registered a mocked response to this oracle call then we prefer responding
// with that.

let mock = self
.mocked_responses
.get_mut(response_position)
.expect("Invalid position of mocked response");

mock.last_called_params = Some(foreign_call.inputs.clone());

let result = mock.result.values.clone();

if let Some(times_left) = &mut mock.times_left {
*times_left -= 1;
if *times_left == 0 {
self.mocked_responses.remove(response_position);
}
}

Ok(result.into())
} else {
Err(ForeignCallError::NoHandler(foreign_call_name.to_string()))
}
}
}
}
}
Loading

0 comments on commit 890b17e

Please sign in to comment.