Skip to content

Commit

Permalink
feat: Contract events in artifacts (#2873)
Browse files Browse the repository at this point in the history
  • Loading branch information
sirasistant authored Sep 28, 2023
1 parent 0397dea commit 4765c82
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 10 deletions.
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>,
}

/// 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

0 comments on commit 4765c82

Please sign in to comment.