Skip to content

Commit b3c12a9

Browse files
sirasistantSakapoi
authored andcommitted
feat: Contract events in artifacts (noir-lang#2873)
1 parent e2a1c26 commit b3c12a9

9 files changed

Lines changed: 90 additions & 10 deletions

File tree

compiler/noirc_driver/src/contract.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
33

44
use acvm::acir::circuit::Circuit;
55
use fm::FileId;
6-
use noirc_abi::Abi;
6+
use noirc_abi::{Abi, ContractEvent};
77
use noirc_errors::debug_info::DebugInfo;
88

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

37+
/// All the events defined inside the contract scope.
38+
/// An event is a struct value that can be emitted via oracles
39+
/// by any contract function during execution.
40+
pub events: Vec<ContractEvent>,
41+
3742
pub file_map: BTreeMap<FileId, DebugFile>,
3843
}
3944

compiler/noirc_driver/src/lib.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use clap::Args;
77
use debug::filter_relevant_files;
88
use fm::FileId;
9-
use noirc_abi::{AbiParameter, AbiType};
9+
use noirc_abi::{AbiParameter, AbiType, ContractEvent};
1010
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
1111
use noirc_evaluator::{create_circuit, into_abi_params};
1212
use noirc_frontend::graph::{CrateId, CrateName};
@@ -285,7 +285,20 @@ fn compile_contract_inner(
285285
let debug_infos: Vec<_> = functions.iter().map(|function| function.debug.clone()).collect();
286286
let file_map = filter_relevant_files(&debug_infos, &context.file_manager);
287287

288-
Ok(CompiledContract { name: contract.name, functions, file_map })
288+
Ok(CompiledContract {
289+
name: contract.name,
290+
events: contract
291+
.events
292+
.iter()
293+
.map(|event_id| {
294+
let typ = context.def_interner.get_struct(*event_id);
295+
let typ = typ.borrow();
296+
ContractEvent::from_struct_type(context, &typ)
297+
})
298+
.collect(),
299+
functions,
300+
file_map,
301+
})
289302
} else {
290303
Err(errors)
291304
}

compiler/noirc_frontend/src/hir/def_map/mod.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::graph::CrateId;
22
use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector};
33
use crate::hir::Context;
4-
use crate::node_interner::{FuncId, NodeInterner};
4+
use crate::node_interner::{FuncId, NodeInterner, StructId};
55
use crate::parser::{parse_program, ParsedModule, ParserError};
6-
use crate::token::{FunctionAttribute, TestScope};
6+
use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope};
77
use arena::{Arena, Index};
88
use fm::{FileId, FileManager};
99
use noirc_errors::Location;
@@ -182,8 +182,20 @@ impl CrateDefMap {
182182
})
183183
.collect();
184184

185+
let events = module
186+
.type_definitions()
187+
.filter_map(|id| {
188+
id.as_type().filter(|struct_id| {
189+
interner
190+
.struct_attributes(struct_id)
191+
.iter()
192+
.any(|attr| attr == &SecondaryAttribute::Event)
193+
})
194+
})
195+
.collect();
196+
185197
let name = self.get_module_path(id, module.parent);
186-
Some(Contract { name, location: module.location, functions })
198+
Some(Contract { name, location: module.location, functions, events })
187199
} else {
188200
None
189201
}
@@ -236,13 +248,14 @@ pub struct ContractFunctionMeta {
236248
pub is_entry_point: bool,
237249
}
238250

239-
/// A 'contract' in Noir source code with the given name and functions.
251+
/// A 'contract' in Noir source code with a given name, functions and events.
240252
/// This is not an AST node, it is just a convenient form to return for CrateDefMap::get_all_contracts.
241253
pub struct Contract {
242254
/// To keep `name` semi-unique, it is prefixed with the names of parent modules via CrateDefMap::get_module_path
243255
pub name: String,
244256
pub location: Location,
245257
pub functions: Vec<ContractFunctionMeta>,
258+
pub events: Vec<StructId>,
246259
}
247260

248261
/// Given a FileId, fetch the File, from the FileManager and parse it's content

compiler/noirc_frontend/src/hir/def_map/module_data.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ impl ModuleData {
9797
self.scope.find_name(name)
9898
}
9999

100+
pub fn type_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
101+
self.definitions.types().values().map(|(id, _)| *id)
102+
}
103+
100104
/// Return an iterator over all definitions defined within this module,
101105
/// excluding any type definitions.
102106
pub fn value_definitions(&self) -> impl Iterator<Item = ModuleDefId> + '_ {

compiler/noirc_frontend/src/lexer/token.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ impl Attribute {
469469
["contract_library_method"] => {
470470
Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod)
471471
}
472+
["event"] => Attribute::Secondary(SecondaryAttribute::Event),
472473
["deprecated", name] => {
473474
if !name.starts_with('"') && !name.ends_with('"') {
474475
return Err(LexerErrorKind::MalformedFuncAttribute {
@@ -546,6 +547,7 @@ pub enum SecondaryAttribute {
546547
// is a helper method for a contract and should not be seen as
547548
// the entry point.
548549
ContractLibraryMethod,
550+
Event,
549551
Custom(String),
550552
}
551553

@@ -558,6 +560,7 @@ impl fmt::Display for SecondaryAttribute {
558560
}
559561
SecondaryAttribute::Custom(ref k) => write!(f, "#[{k}]"),
560562
SecondaryAttribute::ContractLibraryMethod => write!(f, "#[contract_library_method]"),
563+
SecondaryAttribute::Event => write!(f, "#[event]"),
561564
}
562565
}
563566
}
@@ -580,6 +583,7 @@ impl AsRef<str> for SecondaryAttribute {
580583
SecondaryAttribute::Deprecated(None) => "",
581584
SecondaryAttribute::Custom(string) => string,
582585
SecondaryAttribute::ContractLibraryMethod => "",
586+
SecondaryAttribute::Event => "",
583587
}
584588
}
585589
}

compiler/noirc_frontend/src/node_interner.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::hir_def::{
1919
function::{FuncMeta, HirFunction},
2020
stmt::HirStatement,
2121
};
22-
use crate::token::Attributes;
22+
use crate::token::{Attributes, SecondaryAttribute};
2323
use crate::{
2424
ContractFunctionType, FunctionDefinition, Generics, Shared, TypeAliasType, TypeBinding,
2525
TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, Visibility,
@@ -32,6 +32,8 @@ pub struct TraitImplKey {
3232
// pub generics: Generics - TODO
3333
}
3434

35+
type StructAttributes = Vec<SecondaryAttribute>;
36+
3537
/// The node interner is the central storage location of all nodes in Noir's Hir (the
3638
/// various node types can be found in hir_def). The interner is also used to collect
3739
/// extra information about the Hir, such as the type of each node, information about
@@ -73,6 +75,7 @@ pub struct NodeInterner {
7375
// methods from impls to the type.
7476
structs: HashMap<StructId, Shared<StructType>>,
7577

78+
struct_attributes: HashMap<StructId, StructAttributes>,
7679
// Type Aliases map.
7780
//
7881
// Map type aliases to the actual type.
@@ -365,6 +368,7 @@ impl Default for NodeInterner {
365368
definitions: vec![],
366369
id_to_type: HashMap::new(),
367370
structs: HashMap::new(),
371+
struct_attributes: HashMap::new(),
368372
type_aliases: Vec::new(),
369373
traits: HashMap::new(),
370374
trait_implementations: HashMap::new(),
@@ -456,6 +460,7 @@ impl NodeInterner {
456460

457461
let new_struct = StructType::new(struct_id, name, typ.struct_def.span, no_fields, generics);
458462
self.structs.insert(struct_id, Shared::new(new_struct));
463+
self.struct_attributes.insert(struct_id, typ.struct_def.attributes.clone());
459464
struct_id
460465
}
461466

@@ -678,6 +683,10 @@ impl NodeInterner {
678683
&self.function_modifiers[func_id].attributes
679684
}
680685

686+
pub fn struct_attributes(&self, struct_id: &StructId) -> &StructAttributes {
687+
&self.struct_attributes[struct_id]
688+
}
689+
681690
/// Returns the interned statement corresponding to `stmt_id`
682691
pub fn statement(&self, stmt_id: &StmtId) -> HirStatement {
683692
let def =

tooling/nargo/src/artifacts/contract.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use acvm::acir::circuit::Circuit;
2-
use noirc_abi::Abi;
2+
use noirc_abi::{Abi, ContractEvent};
33
use noirc_driver::ContractFunctionType;
44
use serde::{Deserialize, Serialize};
55

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

2123
/// Each function in the contract will be compiled as a separate noir program.

tooling/nargo_cli/src/cli/compile_cmd.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ fn save_contract(
267267
name: contract.name,
268268
backend: String::from(BACKEND_IDENTIFIER),
269269
functions: preprocessed_functions,
270+
events: contract.events,
270271
};
271272

272273
save_contract_to_file(

tooling/noirc_abi/src/lib.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use acvm::{
1212
use errors::AbiError;
1313
use input_parser::InputValue;
1414
use iter_extended::{try_btree_map, try_vecmap, vecmap};
15-
use noirc_frontend::{hir::Context, Signedness, Type, TypeBinding, TypeVariableKind, Visibility};
15+
use noirc_frontend::{
16+
hir::Context, Signedness, StructType, Type, TypeBinding, TypeVariableKind, Visibility,
17+
};
1618
use serde::{Deserialize, Serialize};
1719
// This is the ABI used to bridge the different TOML formats for the initial
1820
// witness, the partial witness generator and the interpreter.
@@ -477,6 +479,33 @@ fn decode_string_value(field_elements: &[FieldElement]) -> String {
477479
final_string.to_owned()
478480
}
479481

482+
#[derive(Debug, Serialize, Deserialize)]
483+
pub struct ContractEvent {
484+
/// Event name
485+
name: String,
486+
/// The fully qualified path to the event definition
487+
path: String,
488+
489+
/// Fields of the event
490+
#[serde(
491+
serialize_with = "serialization::serialize_struct_fields",
492+
deserialize_with = "serialization::deserialize_struct_fields"
493+
)]
494+
fields: Vec<(String, AbiType)>,
495+
}
496+
497+
impl ContractEvent {
498+
pub fn from_struct_type(context: &Context, struct_type: &StructType) -> Self {
499+
let fields = vecmap(struct_type.get_fields(&[]), |(name, typ)| {
500+
(name, AbiType::from_type(context, &typ))
501+
});
502+
// For the ABI, we always want to resolve the struct paths from the root crate
503+
let path = context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id);
504+
505+
Self { name: struct_type.name.0.contents.clone(), path, fields }
506+
}
507+
}
508+
480509
#[cfg(test)]
481510
mod test {
482511
use std::collections::BTreeMap;

0 commit comments

Comments
 (0)