Skip to content
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: Contract events in artifacts #2873

Merged
merged 4 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion compiler/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::BTreeMap;

use acvm::acir::circuit::Circuit;
use fm::FileId;
use noirc_abi::Abi;
use noirc_abi::{Abi, ContractEvent};
use noirc_errors::debug_info::DebugInfo;

use super::debug::DebugFile;
Expand Down Expand Up @@ -34,6 +34,11 @@ pub struct CompiledContract {
/// stored in this `Vector`.
pub functions: Vec<ContractFunction>,

/// All the events defined inside the contract scope.
/// An event is a struct value that can be emitted via oracles
/// by any contract function during execution.
pub events: Vec<ContractEvent>,

pub file_map: BTreeMap<FileId, DebugFile>,
}

Expand Down
17 changes: 15 additions & 2 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use clap::Args;
use debug::filter_relevant_files;
use fm::FileId;
use noirc_abi::{AbiParameter, AbiType};
use noirc_abi::{AbiParameter, AbiType, ContractEvent};
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
use noirc_evaluator::{create_circuit, into_abi_params};
use noirc_frontend::graph::{CrateId, CrateName};
Expand Down Expand Up @@ -166,7 +166,7 @@
let compiled_program = compile_no_check(context, options, main, cached_program)?;

if options.print_acir {
println!("Compiled ACIR for main (unoptimized):");

Check warning on line 169 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
println!("{}", compiled_program.circuit);
}

Expand Down Expand Up @@ -215,7 +215,7 @@
if options.print_acir {
for contract_function in &compiled_contract.functions {
println!(
"Compiled ACIR for {}::{} (unoptimized):",

Check warning on line 218 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
compiled_contract.name, contract_function.name
);
println!("{}", contract_function.bytecode);
Expand Down Expand Up @@ -285,7 +285,20 @@
let debug_infos: Vec<_> = functions.iter().map(|function| function.debug.clone()).collect();
let file_map = filter_relevant_files(&debug_infos, &context.file_manager);

Ok(CompiledContract { name: contract.name, functions, file_map })
Ok(CompiledContract {
name: contract.name,
events: contract
.events
.iter()
.map(|event_id| {
let typ = context.def_interner.get_struct(*event_id);
let typ = typ.borrow();
ContractEvent::from_struct_type(context, &typ)
})
.collect(),
functions,
file_map,
})
} else {
Err(errors)
}
Expand All @@ -304,7 +317,7 @@
) -> Result<CompiledProgram, FileDiagnostic> {
let program = monomorphize(main_function, &context.def_interner);

let hash = fxhash::hash64(&program);

Check warning on line 320 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (fxhash)

// If user has specified that they want to see intermediate steps printed then we should
// force compilation even if the program hasn't changed.
Expand Down
21 changes: 17 additions & 4 deletions compiler/noirc_frontend/src/hir/def_map/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::graph::CrateId;
use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector};
use crate::hir::Context;
use crate::node_interner::{FuncId, NodeInterner};
use crate::node_interner::{FuncId, NodeInterner, StructId};
use crate::parser::{parse_program, ParsedModule, ParserError};
use crate::token::{FunctionAttribute, TestScope};
use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope};
use arena::{Arena, Index};
use fm::{FileId, FileManager};
use noirc_errors::Location;
Expand Down Expand Up @@ -182,8 +182,20 @@ impl CrateDefMap {
})
.collect();

let events = module
.type_definitions()
.filter_map(|id| {
id.as_type().filter(|struct_id| {
interner
.struct_attributes(struct_id)
.iter()
.any(|attr| attr == &SecondaryAttribute::Event)
})
})
.collect();

let name = self.get_module_path(id, module.parent);
Some(Contract { name, location: module.location, functions })
Some(Contract { name, location: module.location, functions, events })
} else {
None
}
Expand Down Expand Up @@ -236,13 +248,14 @@ pub struct ContractFunctionMeta {
pub is_entry_point: bool,
}

/// A 'contract' in Noir source code with the given name and functions.
/// A 'contract' in Noir source code with a given name, functions and events.
/// This is not an AST node, it is just a convenient form to return for CrateDefMap::get_all_contracts.
pub struct Contract {
/// To keep `name` semi-unique, it is prefixed with the names of parent modules via CrateDefMap::get_module_path
pub name: String,
pub location: Location,
pub functions: Vec<ContractFunctionMeta>,
pub events: Vec<StructId>,
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
}

/// Given a FileId, fetch the File, from the FileManager and parse it's content
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/hir/def_map/module_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ impl ModuleData {
self.scope.find_name(name)
}

pub fn type_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
self.definitions.types().values().map(|(id, _)| *id)
}

/// Return an iterator over all definitions defined within this module,
/// excluding any type definitions.
pub fn value_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@
match string.trim() {
"should_fail" => Some(TestScope::ShouldFailWith { reason: None }),
s if s.starts_with("should_fail_with") => {
let parts: Vec<&str> = s.splitn(2, '=').collect();

Check warning on line 335 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (splitn)
if parts.len() == 2 {
let reason = parts[1].trim();
let reason = reason.trim_matches('"');
Expand Down Expand Up @@ -467,6 +467,7 @@
["contract_library_method"] => {
Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod)
}
["event"] => Attribute::Secondary(SecondaryAttribute::Event),
["deprecated", name] => {
if !name.starts_with('"') && !name.ends_with('"') {
return Err(LexerErrorKind::MalformedFuncAttribute {
Expand Down Expand Up @@ -544,6 +545,7 @@
// is a helper method for a contract and should not be seen as
// the entry point.
ContractLibraryMethod,
Event,
Custom(String),
}

Expand All @@ -556,6 +558,7 @@
}
SecondaryAttribute::Custom(ref k) => write!(f, "#[{k}]"),
SecondaryAttribute::ContractLibraryMethod => write!(f, "#[contract_library_method]"),
SecondaryAttribute::Event => write!(f, "#[event]"),
}
}
}
Expand All @@ -578,6 +581,7 @@
SecondaryAttribute::Deprecated(None) => "",
SecondaryAttribute::Custom(string) => string,
SecondaryAttribute::ContractLibraryMethod => "",
SecondaryAttribute::Event => "",
}
}
}
Expand Down Expand Up @@ -642,7 +646,7 @@
Keyword::Field => write!(f, "Field"),
Keyword::Fn => write!(f, "fn"),
Keyword::For => write!(f, "for"),
Keyword::FormatString => write!(f, "fmtstr"),

Check warning on line 649 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (fmtstr)
Keyword::Global => write!(f, "global"),
Keyword::If => write!(f, "if"),
Keyword::Impl => write!(f, "impl"),
Expand Down Expand Up @@ -685,7 +689,7 @@
"Field" => Keyword::Field,
"fn" => Keyword::Fn,
"for" => Keyword::For,
"fmtstr" => Keyword::FormatString,

Check warning on line 692 in compiler/noirc_frontend/src/lexer/token.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (fmtstr)
"global" => Keyword::Global,
"if" => Keyword::If,
"impl" => Keyword::Impl,
Expand Down
11 changes: 10 additions & 1 deletion compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
function::{FuncMeta, HirFunction},
stmt::HirStatement,
};
use crate::token::Attributes;
use crate::token::{Attributes, SecondaryAttribute};
use crate::{
ContractFunctionType, FunctionDefinition, Generics, Shared, TypeAliasType, TypeBinding,
TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, Visibility,
Expand All @@ -32,6 +32,8 @@
// pub generics: Generics - TODO
}

type StructAttributes = Vec<SecondaryAttribute>;

/// The node interner is the central storage location of all nodes in Noir's Hir (the
/// various node types can be found in hir_def). The interner is also used to collect
/// extra information about the Hir, such as the type of each node, information about
Expand Down Expand Up @@ -73,6 +75,7 @@
// methods from impls to the type.
structs: HashMap<StructId, Shared<StructType>>,

struct_attributes: HashMap<StructId, StructAttributes>,
// Type Aliases map.
//
// Map type aliases to the actual type.
Expand Down Expand Up @@ -115,7 +118,7 @@
}

/// All the information from a function that is filled out during definition collection rather than
/// name resolution. Resultingly, if information about a function is needed during name resolution,

Check warning on line 121 in compiler/noirc_frontend/src/node_interner.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (Resultingly)
/// this is the only place where it is safe to retrieve it (where all fields are guaranteed to be initialized).
pub struct FunctionModifiers {
pub name: String,
Expand Down Expand Up @@ -365,6 +368,7 @@
definitions: vec![],
id_to_type: HashMap::new(),
structs: HashMap::new(),
struct_attributes: HashMap::new(),
type_aliases: Vec::new(),
traits: HashMap::new(),
trait_implementations: HashMap::new(),
Expand Down Expand Up @@ -456,6 +460,7 @@

let new_struct = StructType::new(struct_id, name, typ.struct_def.span, no_fields, generics);
self.structs.insert(struct_id, Shared::new(new_struct));
self.struct_attributes.insert(struct_id, typ.struct_def.attributes.clone());
struct_id
}

Expand Down Expand Up @@ -578,7 +583,7 @@
id
}

/// Push a function with the default modifiers and moduleid for testing

Check warning on line 586 in compiler/noirc_frontend/src/node_interner.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (moduleid)
#[cfg(test)]
pub fn push_test_function_definition(&mut self, name: String) -> FuncId {
let id = self.push_fn(HirFunction::empty());
Expand Down Expand Up @@ -678,6 +683,10 @@
&self.function_modifiers[func_id].attributes
}

pub fn struct_attributes(&self, struct_id: &StructId) -> &StructAttributes {
&self.struct_attributes[struct_id]
}

/// Returns the interned statement corresponding to `stmt_id`
pub fn statement(&self, stmt_id: &StmtId) -> HirStatement {
let def =
Expand Down
4 changes: 3 additions & 1 deletion tooling/nargo/src/artifacts/contract.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use acvm::acir::circuit::Circuit;
use noirc_abi::Abi;
use noirc_abi::{Abi, ContractEvent};
use noirc_driver::ContractFunctionType;
use serde::{Deserialize, Serialize};

Expand All @@ -16,6 +16,8 @@ pub struct PreprocessedContract {
pub backend: String,
/// Each of the contract's functions are compiled into a separate program stored in this `Vec`.
pub functions: Vec<PreprocessedContractFunction>,
/// All the events defined inside the contract scope.
pub events: Vec<ContractEvent>,
}

/// Each function in the contract will be compiled as a separate noir program.
Expand Down
1 change: 1 addition & 0 deletions tooling/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ fn save_contract(
name: contract.name,
backend: String::from(BACKEND_IDENTIFIER),
functions: preprocessed_functions,
events: contract.events,
};

save_contract_to_file(
Expand Down
31 changes: 30 additions & 1 deletion tooling/noirc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use acvm::{
use errors::AbiError;
use input_parser::InputValue;
use iter_extended::{try_btree_map, try_vecmap, vecmap};
use noirc_frontend::{hir::Context, Signedness, Type, TypeBinding, TypeVariableKind, Visibility};
use noirc_frontend::{
hir::Context, Signedness, StructType, Type, TypeBinding, TypeVariableKind, Visibility,
};
use serde::{Deserialize, Serialize};
// This is the ABI used to bridge the different TOML formats for the initial
// witness, the partial witness generator and the interpreter.
Expand Down Expand Up @@ -477,6 +479,33 @@ fn decode_string_value(field_elements: &[FieldElement]) -> String {
final_string.to_owned()
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ContractEvent {
/// Event name
name: String,
/// The fully qualified path to the event definition
path: String,

/// Fields of the event
#[serde(
serialize_with = "serialization::serialize_struct_fields",
deserialize_with = "serialization::deserialize_struct_fields"
)]
fields: Vec<(String, AbiType)>,
}

impl ContractEvent {
pub fn from_struct_type(context: &Context, struct_type: &StructType) -> Self {
let fields = vecmap(struct_type.get_fields(&[]), |(name, typ)| {
(name, AbiType::from_type(context, &typ))
});
// For the ABI, we always want to resolve the struct paths from the root crate
let path = context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id);

Self { name: struct_type.name.0.contents.clone(), path, fields }
}
}

#[cfg(test)]
mod test {
use std::collections::BTreeMap;
Expand Down