Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ fuzz_target!(|code_unit: CodeUnit| {
function: FunctionHandleIndex(0),
visibility: Visibility::Public,
is_entry: false,
// TODO fix this
authenticator_version: None,
acquires_global_resources: vec![],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use std::str::FromStr;
use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use move_binary_format::file_format::{
empty_module, AbilitySet, Bytecode, CodeUnit, Constant, DatatypeHandle, DatatypeHandleIndex,
FieldDefinition, FunctionDefinition, FunctionHandle, FunctionHandleIndex, IdentifierIndex,
ModuleHandleIndex, Signature, SignatureIndex, SignatureToken,
AbilitySet, Bytecode, CodeUnit, Constant, DatatypeHandle, DatatypeHandleIndex, FieldDefinition,
FunctionDefinition, FunctionHandle, FunctionHandleIndex, IdentifierIndex, ModuleHandleIndex,
Signature, SignatureIndex, SignatureToken,
SignatureToken::{Address, Bool},
StructDefinition, StructFieldInformation, TypeSignature, Visibility,
StructDefinition, StructFieldInformation, TypeSignature, Visibility, empty_module,
};
use move_core_types::{account_address::AccountAddress, identifier::Identifier};

Expand Down Expand Up @@ -90,6 +90,8 @@ fuzz_target!(|mix: Mixed| {
function: FunctionHandleIndex(0),
visibility: Visibility::Public,
is_entry: false,
// TOOD fix this
authenticator_version: None,
acquires_global_resources: vec![],
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use move_core_types::account_address::AccountAddress;
use move_ir_to_bytecode::compiler::compile_module;
use move_ir_types::{ast::*, location::*};
use move_symbol_pool::Symbol;
use rand::{rngs::StdRng, Rng};
use rand::{Rng, rngs::StdRng};

use crate::{options::ModuleGeneratorOptions, padding::Pad, utils::random_string};

Expand Down Expand Up @@ -254,6 +254,8 @@ impl<'a> ModuleGenerator<'a> {
loc: Spanned::unsafe_no_loc(()).loc,
visibility: FunctionVisibility::Public,
is_entry: false,
// TOOD fix this
authenticator_version: None,
signature,
body: FunctionBody::Move {
locals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1556,10 +1556,14 @@ fn load_function_def(cursor: &mut VersionedCursor) -> BinaryLoaderResult<Functio
return Err(PartialVMError::new(StatusCode::INVALID_FLAG_BITS));
}

// TODO fix this
let authenticator_version = None;

Ok(FunctionDefinition {
function,
visibility,
is_entry,
authenticator_version,
acquires_global_resources,
code: code_unit,
})
Expand Down Expand Up @@ -2146,7 +2150,8 @@ impl<'a, 'b> VersionedBinary<'a, 'b> {
return Err(PartialVMError::new(StatusCode::UNKNOWN_VERSION));
}

// Bad flavor to the version: for version 7 and above, only IOTA_FLAVOR is supported
// Bad flavor to the version: for version 7 and above, only IOTA_FLAVOR is
// supported
if version >= VERSION_7 && flavor != Some(BinaryFlavor::IOTA_FLAVOR) {
return Err(PartialVMError::new(StatusCode::UNKNOWN_VERSION));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ pub struct FunctionDefinition {
pub visibility: Visibility,
/// Marker if the function is intended as an entry function. That is
pub is_entry: bool,
/// In case the function has been marked by "authenticator(version = x)"
/// attribute, this field will contain the specified version.
/// A value of None indicates that the function was not marked as an
/// authenticator.
pub authenticator_version: Option<u8>,
Copy link
Contributor

@valeriyr valeriyr Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TheMrAI @miker83z, I am leaving this comment just to discuss the idea.
What do you think if we created a struct to represent the authenticator info, for example:

pub struct AuthenticatorInfo {
    pub version: u8,
    ...
}

It will make it possible to conveniently extend the authenticator declaration with additional info:

// `description` can be shown in the explorer, for example.
#[authenticator(version = 2, description = b"User friendly description", ...)]

Probably you have ideas about what we can add at the moment?

/// List of locally defined types (declared in this module) with the `Key`
/// ability that the procedure might access, either through:
/// BorrowGlobal, MoveFrom, or transitively through another procedure
Expand Down Expand Up @@ -2937,6 +2942,7 @@ pub fn basic_test_module() -> CompiledModule {
function: FunctionHandleIndex(0),
visibility: Visibility::Private,
is_entry: false,
authenticator_version: None,
acquires_global_resources: vec![],
code: Some(CodeUnit {
locals: SignatureIndex(0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ impl FunctionDefinitionGen {
function: function_handle,
visibility: self.visibility,
is_entry: self.is_entry,
// TODO fix this
authenticator_version: None,
acquires_global_resources,
code: Some(self.code.materialize(state)),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,8 @@ fn gate_known_attribute(context: &mut Context, loc: Loc, known: &KnownAttribute)
| KnownAttribute::DefinesPrimitive(_)
| KnownAttribute::External(_)
| KnownAttribute::Syntax(_)
| KnownAttribute::Deprecation(_) => (),
| KnownAttribute::Deprecation(_)
| KnownAttribute::Authenticator(_) => (),
KnownAttribute::Error(_) => {
let pkg = context.current_package();
context.check_feature(pkg, FeatureGate::CleverAssertions, loc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,94 @@
// Modifications Copyright (c) 2025 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! Attributes
//!
//! The compiler has a built in system for processing arbitrary attributes,
//! but has no generalized concept of validating or storing them.
//! A fully compiled program will loose all attributes by the end of compilation
//! and while attributes are initially processed and propagated through the
//! stages, they have no effect until they are handled by the developer.
//! This means that adding an attribute stub is simple, but properly handling it
//! may happen on an arbitrary stage of compilation.
//!
//! ## Processing stages
//!
//! The **parser** stage of compilation will extract everything the compiler
//! understands about an attribute. Where is it from, what form does it have,
//! but nothing else is known.
//! During the **expansion** stage an attribute will be turned into a
//! [KnownAttribute] or unknown attribute after it passed the
//! [AttributePosition] check and any associated values will be attached to it.
//! After this point the attributes will propagate through the stages up until
//! **to_bytecode** at which point they will be discarded completely.
//!
//! ## Structure
//!
//! There are tree types of attributes, defined in the **expansion**
//! stage.
//!
//! Named attributes, which only have an identifier to them.
//! ```
//! #[NamedAttribute]
//! ....
//! ```
//! which can be listed in one unit or separated into more lines:
//! ```
//! #[NamedAttribute1, NamedAttribute2]
//! ...
//!
//! #[NamedAttribute1]
//! #[NamedAttribute2]
//! ...
//! ```
//! Assigned attributes, which also have an associated value:
//! ```
//! #[AssignedAttribute = AttributeValue]
//! ...
//! ```
//! which can be listed/grouped similarly to the named version above.
//!
//! Parameterized attributes, which can recursively contain
//! all other types of attributes:
//! ```
//! #[Parameterized(NamedAttribute)]
//! ...
//!
//! #[Parameterized(AssignedAttribute = AttributeValue)]
//! ...
//!
//! #[Parameterized(Parameterized(...))]
//! ...
//! ```
//! which follows the same listing rules as seen above.
//!
//! The compiler ensures that no duplicate attributes are specified
//! and checks if the values fit into a set of allowed types, but
//! there is no further validation. Everything else is up to the
//! developer.
//!
//! ## Attribute implementation patterns
//!
//! Simple **named** and **assigned** attributes are easy to implement and apart
//! from setting the appropriate [AttributePosition] and trait implementations
//! only require a type check to be implemented by the developer.
//!
//! **Parameterized** attributes are considerably trickier to be used properly
//! as they can contain other attribute types recursively, but
//! [AttributePosition] is not capable of expressing that a given attribute may
//! only appear in such a nested structure. This means that after defining the
//! top level **parameterized** attribute it is up the developer to define
//! exactly what internal formats are expected.
//! For example see [TestingAttribute::ExpectedFailure] implementation.

use std::{collections::BTreeSet, fmt};

use once_cell::sync::Lazy;

/// All the code positions at which an attribute may be placed
/// at in code.
///
/// A [KnownAttribute] specifies on which positions it may appear.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AttributePosition {
AddressBlock,
Expand All @@ -19,6 +103,10 @@ pub enum AttributePosition {
Spec,
}

/// The list of attribute types recognized by the compiler.
///
/// These variants not necessarily specify a single attribute
/// , but a whole class of them like [KnownAttribute::Testing].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum KnownAttribute {
Testing(TestingAttribute),
Expand All @@ -30,6 +118,7 @@ pub enum KnownAttribute {
Syntax(SyntaxAttribute),
Error(ErrorAttribute),
Deprecation(DeprecationAttribute),
Authenticator(AuthenticatorAttribute),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
Expand Down Expand Up @@ -80,6 +169,9 @@ pub struct ErrorAttribute;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct DeprecationAttribute;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct AuthenticatorAttribute;

impl AttributePosition {
const ALL: &'static [Self] = &[
Self::AddressBlock,
Expand Down Expand Up @@ -109,6 +201,7 @@ impl KnownAttribute {
SyntaxAttribute::SYNTAX => SyntaxAttribute::Syntax.into(),
ErrorAttribute::ERROR => ErrorAttribute.into(),
DeprecationAttribute::DEPRECATED => DeprecationAttribute.into(),
AuthenticatorAttribute::AUTHENTICATOR => AuthenticatorAttribute.into(),
_ => return None,
})
}
Expand All @@ -124,6 +217,7 @@ impl KnownAttribute {
Self::Syntax(a) => a.name(),
Self::Error(a) => a.name(),
Self::Deprecation(a) => a.name(),
Self::Authenticator(a) => a.name(),
}
}

Expand All @@ -138,6 +232,7 @@ impl KnownAttribute {
Self::Syntax(a) => a.expected_positions(),
Self::Error(a) => a.expected_positions(),
Self::Deprecation(a) => a.expected_positions(),
Self::Authenticator(a) => a.expected_positions(),
}
}
}
Expand Down Expand Up @@ -357,6 +452,21 @@ impl DeprecationAttribute {
}
}

impl AuthenticatorAttribute {
pub const AUTHENTICATOR: &'static str = "authenticator";
pub const VERSION: &'static str = "version";

pub const fn name(&self) -> &str {
Self::AUTHENTICATOR
}

pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
static AUTHENTICATOR_POSITIONS: Lazy<BTreeSet<AttributePosition>> =
Lazy::new(|| BTreeSet::from([AttributePosition::Function]));
&AUTHENTICATOR_POSITIONS
}
}

//**************************************************************************************************
// Display
//**************************************************************************************************
Expand Down Expand Up @@ -389,6 +499,7 @@ impl fmt::Display for KnownAttribute {
Self::Syntax(a) => a.fmt(f),
Self::Error(a) => a.fmt(f),
Self::Deprecation(a) => a.fmt(f),
Self::Authenticator(a) => a.fmt(f),
}
}
}
Expand Down Expand Up @@ -447,6 +558,12 @@ impl fmt::Display for DeprecationAttribute {
}
}

impl fmt::Display for AuthenticatorAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}

//**************************************************************************************************
// From
//**************************************************************************************************
Expand Down Expand Up @@ -496,3 +613,8 @@ impl From<DeprecationAttribute> for KnownAttribute {
Self::Deprecation(a)
}
}
impl From<AuthenticatorAttribute> for KnownAttribute {
fn from(a: AuthenticatorAttribute) -> Self {
Self::Authenticator(a)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use move_core_types::u256::U256;
use move_ir_types::location::Spanned;
use move_symbol_pool::Symbol;

use crate::{
diag,
diagnostics::DiagnosticReporter,
expansion::ast::{Attribute_, AttributeName_, AttributeValue},
shared::{
known_attributes::{AuthenticatorAttribute, KnownAttribute},
unique_map::UniqueMap,
},
};

pub fn parse_authenticator_version(
reporter: &DiagnosticReporter,
attributes: &UniqueMap<Spanned<KnownAttribute>, Spanned<Attribute_>>,
) -> Option<u8> {
let Some(attribute) = attributes.get_(&AuthenticatorAttribute.into()) else {
return None;
};

let sp!(authenticator_loc, value) = attribute;
match value {
Attribute_::Name(_) => Some(1),
Attribute_::Assigned(_, attribute_value) => {
authenticator_version_to_u8(reporter, &attribute_value)
}
Attribute_::Parameterized(_, inner_attributes) => {
let Some(sp!(_, version_value)) = inner_attributes.get_(&AttributeName_::Unknown(
Symbol::from(AuthenticatorAttribute::VERSION),
)) else {
reporter.add_diag(diag!(
Attributes::InvalidValue,
(
*authenticator_loc,
"Missing `version` for authenticator attribute. Expected format: #[authenticator(version = ...)]".to_string()
)
));
return None;
};

match version_value {
Attribute_::Name(_) | Attribute_::Parameterized(_, _) => None,
Attribute_::Assigned(_, attribute_value) => {
authenticator_version_to_u8(reporter, &attribute_value)
}
}
}
}
}

fn authenticator_version_to_u8(
reporter: &DiagnosticReporter,
attribute_value: &AttributeValue,
) -> Option<u8> {
use crate::expansion::ast::{AttributeValue_ as EAV, Value_ as EV};

match attribute_value {
sp!(_, EAV::Value(sp!(_, EV::U8(value)))) => Some(*value),
sp!(_, EAV::Value(sp!(_, EV::InferredNum(value)))) if *value <= U256::from(u8::MAX) => {
Some(value.down_cast_lossy())
}
// As a catch all, we reject all other supported attribute value types.
sp!(_, _) => {
reporter.add_diag(diag!(
Attributes::InvalidValue,
(
attribute_value.loc,
"Only unannotated or u8 literal `version` values are supported.".to_string()
),
));
None
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ mod canonicalize_handles;
#[macro_use]
mod context;
mod optimize;
mod authenticator_attribute;
pub mod translate;
Loading