Skip to main content

miden_standards/account/auth/
singlesig.rs

1use miden_protocol::Word;
2use miden_protocol::account::auth::{AuthScheme, PublicKey};
3use miden_protocol::account::component::{
4    AccountComponentCode,
5    AccountComponentMetadata,
6    SchemaType,
7    StorageSchema,
8    StorageSlotSchema,
9};
10use miden_protocol::account::{
11    AccountComponent,
12    AccountComponentName,
13    StorageSlot,
14    StorageSlotName,
15};
16use miden_protocol::crypto::dsa::{ecdsa_k256_keccak, falcon512_poseidon2};
17use miden_protocol::utils::sync::LazyLock;
18
19use super::Approver;
20use crate::account::account_component_code;
21
22account_component_code!(SINGLESIG_CODE, "miden-standards-auth-singlesig.masp");
23
24// CONSTANTS
25// ================================================================================================
26
27static PUBKEY_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
28    StorageSlotName::new("miden::standards::auth::singlesig::pub_key")
29        .expect("storage slot name should be valid")
30});
31
32static SCHEME_ID_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
33    StorageSlotName::new("miden::standards::auth::singlesig::scheme")
34        .expect("storage slot name should be valid")
35});
36
37/// An [`AccountComponent`] implementing the signature scheme for authentication
38/// of transactions.
39///
40/// This component exports `auth_tx`, which loads the public key and signature scheme id from
41/// storage and delegates transaction authentication to
42/// `miden::standards::auth::signature::authenticate_transaction`.
43///
44/// Before authenticating, `auth_tx` pays the transaction fee via
45/// `miden::standards::fee::pay_fee`: it creates a public TX_FEE note (see
46/// [`TxFeeNote`](crate::note::TxFeeNote)) funded from the account's vault, so on
47/// fee-charging chains the account must hold a sufficient balance of the payment asset. The
48/// payment asset and conversion rate are committed to via the transaction's auth args (see
49/// [`FeeConversionInfo`](crate::account::auth::FeeConversionInfo); native fee asset at rate 1/1 for
50/// plain native payment). On chains with a zero verification base fee no note is created. The
51/// fee note is created before the transaction summary, so it is covered by the signature.
52///
53/// When linking against this component, the `miden::standards` library must be available to the
54/// assembler (which also implies availability of `miden::protocol`). This is the case when using
55/// [`CodeBuilder`][builder].
56///
57/// [builder]: crate::code_builder::CodeBuilder
58pub struct AuthSingleSig {
59    approver: Approver,
60}
61
62impl AuthSingleSig {
63    /// The name of the component.
64    pub const NAME: &'static str = "miden::standards::auth::singlesig";
65
66    /// Returns the canonical [`AccountComponentName`] of this component.
67    pub const fn name() -> AccountComponentName {
68        AccountComponentName::from_static_str(Self::NAME)
69    }
70
71    /// Returns the [`AccountComponentCode`] of this component.
72    pub fn code() -> &'static AccountComponentCode {
73        &SINGLESIG_CODE
74    }
75
76    /// Creates a new [`AuthSingleSig`] component with the given approver.
77    pub fn new(approver: Approver) -> Self {
78        Self { approver }
79    }
80
81    /// Creates a new [`AuthSingleSig`] component using the Falcon512Poseidon2 signature scheme.
82    ///
83    /// The public key commitment is derived from the provided Falcon512 public key.
84    pub fn falcon512_poseidon2(pub_key: falcon512_poseidon2::PublicKey) -> Self {
85        Self {
86            approver: Approver::new(pub_key.into(), AuthScheme::Falcon512Poseidon2),
87        }
88    }
89
90    /// Creates a new [`AuthSingleSig`] component using the EcdsaK256Keccak signature scheme.
91    ///
92    /// The public key commitment is derived from the provided ECDSA K256 public key.
93    ///
94    /// Note: this scheme discloses the signer's public key and signature at proving time and
95    /// therefore does not provide public-key privacy. See
96    /// [`AuthScheme::EcdsaK256Keccak`][scheme] for details, and prefer
97    /// [`falcon512_poseidon2`](Self::falcon512_poseidon2) if signer-key privacy is required.
98    ///
99    /// [scheme]: miden_protocol::account::auth::AuthScheme::EcdsaK256Keccak
100    pub fn ecdsa_k256_keccak(pub_key: ecdsa_k256_keccak::PublicKey) -> Self {
101        Self {
102            approver: Approver::new(pub_key.into(), AuthScheme::EcdsaK256Keccak),
103        }
104    }
105
106    /// Creates a new [`AuthSingleSig`] component from a [`PublicKey`].
107    ///
108    /// The authentication scheme and public key commitment are derived from the provided key.
109    pub fn from_public_key(pub_key: PublicKey) -> Self {
110        Self {
111            approver: Approver::new(pub_key.to_commitment(), pub_key.auth_scheme()),
112        }
113    }
114
115    /// Returns the approver of this component.
116    pub fn approver(&self) -> Approver {
117        self.approver
118    }
119
120    /// Returns the [`StorageSlotName`] where the public key is stored.
121    pub fn public_key_slot() -> &'static StorageSlotName {
122        &PUBKEY_SLOT_NAME
123    }
124
125    // Returns the [`StorageSlotName`] where the scheme ID is stored.
126    pub fn scheme_id_slot() -> &'static StorageSlotName {
127        &SCHEME_ID_SLOT_NAME
128    }
129
130    /// Returns the storage slot schema for the public key slot.
131    pub fn public_key_slot_schema() -> (StorageSlotName, StorageSlotSchema) {
132        (
133            Self::public_key_slot().clone(),
134            StorageSlotSchema::value("Public key commitment", SchemaType::pub_key()),
135        )
136    }
137    /// Returns the storage slot schema for the scheme ID slot.
138    pub fn auth_scheme_slot_schema() -> (StorageSlotName, StorageSlotSchema) {
139        (
140            Self::scheme_id_slot().clone(),
141            StorageSlotSchema::value("Scheme ID", SchemaType::auth_scheme()),
142        )
143    }
144
145    /// Returns the [`AccountComponentMetadata`] for this component.
146    pub fn component_metadata() -> AccountComponentMetadata {
147        let storage_schema = StorageSchema::new(vec![
148            Self::public_key_slot_schema(),
149            Self::auth_scheme_slot_schema(),
150        ])
151        .expect("storage schema should be valid");
152
153        AccountComponentMetadata::new(Self::NAME)
154            .with_description(
155                "Authentication component using ECDSA K256 Keccak or Falcon512 Poseidon2 signature scheme",
156            )
157            .with_storage_schema(storage_schema)
158    }
159}
160
161impl From<AuthSingleSig> for AccountComponent {
162    fn from(basic_signature: AuthSingleSig) -> Self {
163        let metadata = AuthSingleSig::component_metadata();
164
165        let storage_slots = vec![
166            StorageSlot::with_value(
167                AuthSingleSig::public_key_slot().clone(),
168                basic_signature.approver.pub_key().into(),
169            ),
170            StorageSlot::with_value(
171                AuthSingleSig::scheme_id_slot().clone(),
172                Word::from([basic_signature.approver.auth_scheme().as_u8(), 0, 0, 0]),
173            ),
174        ];
175
176        AccountComponent::new(AuthSingleSig::code().clone(), storage_slots, metadata).expect(
177            "singlesig component should satisfy the requirements of a valid account component",
178        )
179    }
180}