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 @@ -285,7 +285,20 @@ fn compile_contract_inner(
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 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 @@ -467,6 +467,7 @@ impl Attribute {
["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 @@ pub enum SecondaryAttribute {
// 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 @@ impl fmt::Display for SecondaryAttribute {
}
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 @@ impl AsRef<str> for SecondaryAttribute {
SecondaryAttribute::Deprecated(None) => "",
SecondaryAttribute::Custom(string) => string,
SecondaryAttribute::ContractLibraryMethod => "",
SecondaryAttribute::Event => "",
}
}
}
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 @@ use crate::hir_def::{
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 struct TraitImplKey {
// 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 @@ pub struct NodeInterner {
// 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 @@ -365,6 +368,7 @@ impl Default for NodeInterner {
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 @@ impl NodeInterner {

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 @@ -678,6 +683,10 @@ impl NodeInterner {
&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