Skip to main content

aptos_sdk/transaction/
authenticator.rs

1//! Transaction authenticators.
2
3use crate::types::AccountAddress;
4use serde::ser::{SerializeTuple, SerializeTupleVariant};
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6
7/// Helpers for emitting/consuming raw, length-prefix-free byte runs inside
8/// BCS-serialized structures.
9///
10/// The Aptos on-chain `AccountAuthenticator::{SingleKey, MultiKey}` variants
11/// carry typed fields (e.g. `AnyPublicKey`, `AnySignature`, `MultiKeyPublicKey`,
12/// `MultiKeySignature`, `SingleKeyAuthenticator`) whose BCS encodings already begin
13/// with their own enum/struct tags. When the SDK represents those fields as
14/// `Vec<u8>` of pre-encoded bytes, the default serde-BCS impl wraps each `Vec<u8>`
15/// with another ULEB128 length prefix, producing wire bytes the on-chain
16/// deserializer rejects.
17///
18/// `serialize_tuple(len)` in `aptos_bcs` emits its elements without a length prefix,
19/// which is exactly what we need.
20fn serialize_raw_bytes<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
21    // For empty payloads we can't open a 0-element tuple in some serializers,
22    // but BCS handles `serialize_tuple(0)` fine -- it produces no bytes.
23    let mut tup = serializer.serialize_tuple(bytes.len())?;
24    for byte in bytes {
25        tup.serialize_element(byte)?;
26    }
27    tup.end()
28}
29
30/// Ed25519 public key (32 bytes).
31/// Serializes WITH a length prefix as required by Aptos BCS format.
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct Ed25519PublicKey(pub [u8; 32]);
34
35impl Serialize for Ed25519PublicKey {
36    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
37        // Aptos BCS format requires a length prefix for public keys
38        // Use serde_bytes to serialize with ULEB128 length prefix
39        serde_bytes::Bytes::new(&self.0).serialize(serializer)
40    }
41}
42
43impl<'de> Deserialize<'de> for Ed25519PublicKey {
44    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
45        // Deserialize with length prefix
46        let bytes: Vec<u8> = serde_bytes::deserialize(deserializer)?;
47        if bytes.len() != 32 {
48            return Err(serde::de::Error::invalid_length(bytes.len(), &"32 bytes"));
49        }
50        let mut arr = [0u8; 32];
51        arr.copy_from_slice(&bytes);
52        Ok(Ed25519PublicKey(arr))
53    }
54}
55
56impl From<Vec<u8>> for Ed25519PublicKey {
57    /// Converts a `Vec<u8>` to `Ed25519PublicKey`.
58    ///
59    /// # Panics
60    ///
61    /// Panics if the input is not exactly 32 bytes. Use `Ed25519PublicKey::try_from_bytes`
62    /// for fallible conversion.
63    fn from(bytes: Vec<u8>) -> Self {
64        assert!(
65            bytes.len() == 32,
66            "Ed25519PublicKey requires exactly 32 bytes, got {}",
67            bytes.len()
68        );
69        let mut arr = [0u8; 32];
70        arr.copy_from_slice(&bytes);
71        Ed25519PublicKey(arr)
72    }
73}
74
75impl Ed25519PublicKey {
76    /// Attempts to create an `Ed25519PublicKey` from a byte slice.
77    ///
78    /// Returns an error if the input is not exactly 32 bytes.
79    ///
80    /// # Errors
81    ///
82    /// Returns an error if the input slice is not exactly 32 bytes.
83    pub fn try_from_bytes(bytes: &[u8]) -> crate::error::AptosResult<Self> {
84        if bytes.len() != 32 {
85            return Err(crate::error::AptosError::InvalidPublicKey(format!(
86                "Ed25519PublicKey requires exactly 32 bytes, got {}",
87                bytes.len()
88            )));
89        }
90        let mut arr = [0u8; 32];
91        arr.copy_from_slice(bytes);
92        Ok(Ed25519PublicKey(arr))
93    }
94}
95
96/// Ed25519 signature (64 bytes).
97/// Serializes WITH a length prefix as required by Aptos BCS format.
98#[derive(Clone, Debug, PartialEq, Eq)]
99pub struct Ed25519Signature(pub [u8; 64]);
100
101impl Serialize for Ed25519Signature {
102    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
103        // Aptos BCS format requires a length prefix for signatures
104        // Use serde_bytes to serialize with ULEB128 length prefix
105        serde_bytes::Bytes::new(&self.0).serialize(serializer)
106    }
107}
108
109impl<'de> Deserialize<'de> for Ed25519Signature {
110    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
111        // Deserialize with length prefix
112        let bytes: Vec<u8> = serde_bytes::deserialize(deserializer)?;
113        if bytes.len() != 64 {
114            return Err(serde::de::Error::invalid_length(bytes.len(), &"64 bytes"));
115        }
116        let mut arr = [0u8; 64];
117        arr.copy_from_slice(&bytes);
118        Ok(Ed25519Signature(arr))
119    }
120}
121
122impl From<Vec<u8>> for Ed25519Signature {
123    /// Converts a `Vec<u8>` to `Ed25519Signature`.
124    ///
125    /// # Panics
126    ///
127    /// Panics if the input is not exactly 64 bytes. Use `Ed25519Signature::try_from_bytes`
128    /// for fallible conversion.
129    fn from(bytes: Vec<u8>) -> Self {
130        assert!(
131            bytes.len() == 64,
132            "Ed25519Signature requires exactly 64 bytes, got {}",
133            bytes.len()
134        );
135        let mut arr = [0u8; 64];
136        arr.copy_from_slice(&bytes);
137        Ed25519Signature(arr)
138    }
139}
140
141impl Ed25519Signature {
142    /// Attempts to create an `Ed25519Signature` from a byte slice.
143    ///
144    /// # Errors
145    ///
146    /// Returns an error if the input is not exactly 64 bytes.
147    pub fn try_from_bytes(bytes: &[u8]) -> crate::error::AptosResult<Self> {
148        if bytes.len() != 64 {
149            return Err(crate::error::AptosError::InvalidSignature(format!(
150                "Ed25519Signature requires exactly 64 bytes, got {}",
151                bytes.len()
152            )));
153        }
154        let mut arr = [0u8; 64];
155        arr.copy_from_slice(bytes);
156        Ok(Ed25519Signature(arr))
157    }
158}
159
160/// An authenticator for a transaction.
161///
162/// This contains the signature(s) and public key(s) that prove
163/// the transaction was authorized by the sender.
164///
165/// Note: Variant indices must match Aptos core for BCS compatibility:
166/// - 0: Ed25519
167/// - 1: `MultiEd25519`
168/// - 2: `MultiAgent`
169/// - 3: `FeePayer`
170/// - 4: `SingleSender` (for unified key support)
171#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
172pub enum TransactionAuthenticator {
173    /// Ed25519 single-key authentication (variant 0).
174    Ed25519 {
175        /// The Ed25519 public key (32 bytes).
176        public_key: Ed25519PublicKey,
177        /// The Ed25519 signature (64 bytes).
178        signature: Ed25519Signature,
179    },
180    /// Multi-Ed25519 authentication (variant 1).
181    MultiEd25519 {
182        /// The multi-Ed25519 public key.
183        public_key: Vec<u8>,
184        /// The multi-Ed25519 signature.
185        signature: Vec<u8>,
186    },
187    /// Multi-agent transaction authentication (variant 2).
188    MultiAgent {
189        /// The sender's authenticator.
190        sender: AccountAuthenticator,
191        /// Secondary signer addresses.
192        secondary_signer_addresses: Vec<AccountAddress>,
193        /// Secondary signers' authenticators.
194        secondary_signers: Vec<AccountAuthenticator>,
195    },
196    /// Fee payer transaction authentication (variant 3).
197    FeePayer {
198        /// The sender's authenticator.
199        sender: AccountAuthenticator,
200        /// Secondary signer addresses.
201        secondary_signer_addresses: Vec<AccountAddress>,
202        /// Secondary signers' authenticators.
203        secondary_signers: Vec<AccountAuthenticator>,
204        /// The fee payer's address.
205        fee_payer_address: AccountAddress,
206        /// The fee payer's authenticator.
207        fee_payer_signer: AccountAuthenticator,
208    },
209    /// Single sender authentication with account authenticator (variant 4).
210    /// Used for newer single-key and multi-key accounts.
211    SingleSender {
212        /// The account authenticator.
213        sender: AccountAuthenticator,
214    },
215}
216
217/// An authenticator for a single account (not the full transaction).
218///
219/// The on-chain BCS schema for the `SingleKey` and `MultiKey`
220/// variants wraps the public key and signature in typed Aptos-core structs
221/// (`SingleKeyAuthenticator`, `MultiKeyAuthenticator`)
222/// whose BCS encodings already begin with their own enum/struct tags.
223/// Internally we still hold pre-encoded `Vec<u8>` (callers produce those via the
224/// `AnyPublicKey`/`AnySignature`/`MultiKeyPublicKey`/`MultiKeySignature` helpers).
225/// To match the on-chain wire format exactly we hand-roll the `Serialize`
226/// implementation so those variants emit the inner bytes inline -- without the
227/// extra ULEB128 length prefix that the derive impl would add to a `Vec<u8>` field.
228#[derive(Clone, Debug, PartialEq, Eq)]
229pub enum AccountAuthenticator {
230    /// Ed25519 authentication (variant 0).
231    Ed25519 {
232        /// The public key (32 bytes).
233        public_key: Ed25519PublicKey,
234        /// The signature (64 bytes).
235        signature: Ed25519Signature,
236    },
237    /// Multi-Ed25519 authentication (variant 1).
238    MultiEd25519 {
239        /// The public key.
240        public_key: Vec<u8>,
241        /// The signature.
242        signature: Vec<u8>,
243    },
244    /// Single-key authentication (ed25519, secp256k1 and secp256r1) (variant 2).
245    SingleKey {
246        /// The public key (BCS-serialized `AnyPublicKey`).
247        public_key: Vec<u8>,
248        /// The signature (BCS-serialized `AnySignature`).
249        signature: Vec<u8>,
250    },
251    /// Multi-key authentication (mixed signature types) (variant 3).
252    MultiKey {
253        /// The public key (BCS-serialized `MultiKeyPublicKey`).
254        public_key: Vec<u8>,
255        /// The signature (BCS-serialized `MultiKeySignature`).
256        signature: Vec<u8>,
257    },
258    /// No account authenticator used for simulation only (variant 4).
259    NoAccountAuthenticator,
260}
261
262// Tag values must match the order of the on-chain Rust enum, exactly.
263const ACCOUNT_AUTH_TAG_ED25519: u32 = 0;
264const ACCOUNT_AUTH_TAG_MULTI_ED25519: u32 = 1;
265const ACCOUNT_AUTH_TAG_SINGLE_KEY: u32 = 2;
266const ACCOUNT_AUTH_TAG_MULTI_KEY: u32 = 3;
267const ACCOUNT_AUTH_TAG_NO_ACCOUNT: u32 = 4;
268
269impl Serialize for AccountAuthenticator {
270    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
271        match self {
272            AccountAuthenticator::Ed25519 {
273                public_key,
274                signature,
275            } => {
276                // Ed25519 carries strongly-typed fields whose Serialize impls already
277                // produce the correct BCS bytes; derive-equivalent emission is fine.
278                let mut tv = serializer.serialize_tuple_variant(
279                    "AccountAuthenticator",
280                    ACCOUNT_AUTH_TAG_ED25519,
281                    "Ed25519",
282                    2,
283                )?;
284                tv.serialize_field(public_key)?;
285                tv.serialize_field(signature)?;
286                tv.end()
287            }
288            AccountAuthenticator::MultiEd25519 {
289                public_key,
290                signature,
291            } => {
292                // On-chain `MultiEd25519PublicKey` and `MultiEd25519Signature` are both
293                // `Vec<u8>`-wrappers, so emitting our `Vec<u8>` fields with a length
294                // prefix matches the wire format.
295                let mut tv = serializer.serialize_tuple_variant(
296                    "AccountAuthenticator",
297                    ACCOUNT_AUTH_TAG_MULTI_ED25519,
298                    "MultiEd25519",
299                    2,
300                )?;
301                tv.serialize_field(public_key)?;
302                tv.serialize_field(signature)?;
303                tv.end()
304            }
305            AccountAuthenticator::SingleKey {
306                public_key,
307                signature,
308            } => {
309                // `SingleKey { authenticator: SingleKeyAuthenticator }`. We emit the inner
310                // `SingleKeyAuthenticator` bytes inline (AnyPublicKey then AnySignature).
311                serialize_account_auth_raw_pair(
312                    serializer,
313                    ACCOUNT_AUTH_TAG_SINGLE_KEY,
314                    "SingleKey",
315                    public_key,
316                    signature,
317                )
318            }
319            AccountAuthenticator::MultiKey {
320                public_key,
321                signature,
322            } => {
323                // `MultiKey { authenticator: MultiKeyAuthenticator }`. Emit the inner
324                // bytes inline (MultiKeyPublicKey then MultiKeySignature).
325                serialize_account_auth_raw_pair(
326                    serializer,
327                    ACCOUNT_AUTH_TAG_MULTI_KEY,
328                    "MultiKey",
329                    public_key,
330                    signature,
331                )
332            }
333            AccountAuthenticator::NoAccountAuthenticator => serializer
334                .serialize_tuple_variant(
335                    "AccountAuthenticator",
336                    ACCOUNT_AUTH_TAG_NO_ACCOUNT,
337                    "NoAccountAuthenticator",
338                    0,
339                )
340                .and_then(SerializeTupleVariant::end),
341        }
342    }
343}
344
345fn serialize_account_auth_raw_pair<S: Serializer>(
346    serializer: S,
347    tag: u32,
348    name: &'static str,
349    public_key: &[u8],
350    signature: &[u8],
351) -> Result<S::Ok, S::Error> {
352    // We model the inner authenticator struct (e.g. `SingleKeyAuthenticator`) as
353    // two raw-byte-runs concatenated together: emitting them as tuple-variant
354    // fields means BCS writes the tag then each field's bytes inline.
355    //
356    // Each raw field is serialized via `serialize_raw_bytes` which uses
357    // `serialize_tuple(len)` -- BCS emits no length prefix for tuples.
358    struct Raw<'a>(&'a [u8]);
359    impl Serialize for Raw<'_> {
360        fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
361            serialize_raw_bytes(self.0, s)
362        }
363    }
364
365    let mut tv = serializer.serialize_tuple_variant("AccountAuthenticator", tag, name, 2)?;
366    tv.serialize_field(&Raw(public_key))?;
367    tv.serialize_field(&Raw(signature))?;
368    tv.end()
369}
370
371impl<'de> Deserialize<'de> for AccountAuthenticator {
372    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
373        // The chain wire format for SingleKey/MultiKey does not include
374        // explicit length prefixes for the inner public_key/signature byte runs
375        // (they are typed BCS structs whose total length is parser-recoverable from
376        // their content). This makes a length-agnostic deserializer non-trivial
377        // and out of scope here -- the SDK only ever *constructs* these
378        // authenticators locally and *serializes* them, never deserializes
379        // foreign on-wire bytes back into them.
380        //
381        // For tests that round-trip the SDK's own representation we deserialize
382        // a stable internal layout that matches the prior derive-based Serialize
383        // implementation: ULEB128(len)-prefixed Vec<u8> fields for the
384        // SingleKey/MultiKey variants. This is sufficient for the
385        // existing test_account_authenticator_*_bcs_roundtrip tests, which
386        // serialize *and* deserialize entirely inside the SDK.
387        #[derive(Deserialize)]
388        enum Compat {
389            Ed25519 {
390                public_key: Ed25519PublicKey,
391                signature: Ed25519Signature,
392            },
393            MultiEd25519 {
394                public_key: Vec<u8>,
395                signature: Vec<u8>,
396            },
397            SingleKey {
398                public_key: Vec<u8>,
399                signature: Vec<u8>,
400            },
401            MultiKey {
402                public_key: Vec<u8>,
403                signature: Vec<u8>,
404            },
405            NoAccountAuthenticator,
406        }
407
408        Compat::deserialize(deserializer).map(|c| match c {
409            Compat::Ed25519 {
410                public_key,
411                signature,
412            } => AccountAuthenticator::Ed25519 {
413                public_key,
414                signature,
415            },
416            Compat::MultiEd25519 {
417                public_key,
418                signature,
419            } => AccountAuthenticator::MultiEd25519 {
420                public_key,
421                signature,
422            },
423            Compat::SingleKey {
424                public_key,
425                signature,
426            } => AccountAuthenticator::SingleKey {
427                public_key,
428                signature,
429            },
430            Compat::MultiKey {
431                public_key,
432                signature,
433            } => AccountAuthenticator::MultiKey {
434                public_key,
435                signature,
436            },
437            Compat::NoAccountAuthenticator => AccountAuthenticator::NoAccountAuthenticator,
438        })
439    }
440}
441
442/// Ed25519 authenticator helper.
443#[derive(Clone, Debug, PartialEq, Eq)]
444pub struct Ed25519Authenticator {
445    /// The public key.
446    pub public_key: Vec<u8>,
447    /// The signature.
448    pub signature: Vec<u8>,
449}
450
451impl Ed25519Authenticator {
452    /// Creates a new Ed25519 authenticator.
453    pub fn new(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
454        Self {
455            public_key,
456            signature,
457        }
458    }
459}
460
461impl From<Ed25519Authenticator> for TransactionAuthenticator {
462    fn from(auth: Ed25519Authenticator) -> Self {
463        TransactionAuthenticator::Ed25519 {
464            public_key: auth.public_key.into(),
465            signature: auth.signature.into(),
466        }
467    }
468}
469
470impl From<Ed25519Authenticator> for AccountAuthenticator {
471    fn from(auth: Ed25519Authenticator) -> Self {
472        AccountAuthenticator::Ed25519 {
473            public_key: auth.public_key.into(),
474            signature: auth.signature.into(),
475        }
476    }
477}
478
479/// Zeroes the Ed25519 signature scalars in a BCS-encoded `MultiEd25519Signature`
480/// blob while preserving the trailing 4-byte signer bitmap (and therefore the
481/// signer count implied by it).
482///
483/// The legacy `MultiEd25519Signature` wire layout is
484/// `sig_0(64) || .. || sig_{m-1}(64) || bitmap(4)` with no per-signature tags, so
485/// keeping the final four bytes intact yields a structurally valid signature whose
486/// signer count and bitmap match the input but whose signature scalars are all
487/// zero. This is what the `/transactions/simulate` endpoint expects: an
488/// authenticator that deserializes correctly but carries an (intentionally)
489/// invalid signature. Zeroing the whole blob -- as a naive length-preserving
490/// zeroing would -- destroys the bitmap and makes the byte length disagree with
491/// the implied signer count, so the fullnode rejects the request at
492/// deserialization instead of accepting it as an invalid signature.
493fn zeroed_multi_ed25519_signature(signature: &[u8]) -> Vec<u8> {
494    let mut out = signature.to_vec();
495    match out.len().checked_sub(4) {
496        // Zero every signature scalar byte, keep the 4-byte bitmap intact.
497        Some(sig_bytes_len) => {
498            for byte in &mut out[..sig_bytes_len] {
499                *byte = 0;
500            }
501        }
502        // Malformed / too short to contain a bitmap: fall back to zeroing the
503        // whole (length-preserving) blob. SDK-produced authenticators never hit
504        // this branch.
505        None => out.iter_mut().for_each(|byte| *byte = 0),
506    }
507    out
508}
509
510/// Rebuilds a BCS-encoded `MultiKeySignature` blob with every inner `AnySignature`
511/// payload zeroed, while preserving the signature count (leading ULEB128), each
512/// signature's variant tag and length framing, and the trailing `BitVec` bitmap.
513///
514/// The `MultiKeySignature` wire layout is
515/// `ULEB128(num_sigs) || (variant || ULEB128(len) || payload).. || ULEB128(4) || bitmap(4)`.
516/// A naive length-preserving zeroing of the whole blob destroys the signature
517/// count, the per-signature variant tags/length prefixes, and the bitmap, so the
518/// fullnode cannot parse it back into a `MultiKeySignature`. Here we parse the
519/// structure, zero only the signature payload bytes (keeping each variant and
520/// byte length), and re-serialize so the result still deserializes into a valid
521/// `MultiKeySignature` with the same signer count and bitmap.
522fn zeroed_multi_key_signature(signature: &[u8]) -> Vec<u8> {
523    use crate::crypto::{AnySignature, MultiKeySignature};
524
525    let Ok(parsed) = MultiKeySignature::from_bytes(signature) else {
526        // Not SDK-produced / unparseable: preserve length as a last resort.
527        return vec![0u8; signature.len()];
528    };
529    let rebuilt: Vec<(u8, AnySignature)> = parsed
530        .signatures()
531        .iter()
532        .map(|(index, sig)| {
533            (
534                *index,
535                AnySignature::new(sig.variant, vec![0u8; sig.bytes.len()]),
536            )
537        })
538        .collect();
539    MultiKeySignature::new(rebuilt)
540        .map_or_else(|_| vec![0u8; signature.len()], |sig| sig.to_bytes())
541}
542
543impl TransactionAuthenticator {
544    /// Creates an Ed25519 authenticator.
545    pub fn ed25519(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
546        Self::Ed25519 {
547            public_key: public_key.into(),
548            signature: signature.into(),
549        }
550    }
551
552    /// Creates a multi-Ed25519 authenticator.
553    pub fn multi_ed25519(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
554        Self::MultiEd25519 {
555            public_key,
556            signature,
557        }
558    }
559
560    /// Creates a multi-agent authenticator.
561    pub fn multi_agent(
562        sender: AccountAuthenticator,
563        secondary_signer_addresses: Vec<AccountAddress>,
564        secondary_signers: Vec<AccountAuthenticator>,
565    ) -> Self {
566        Self::MultiAgent {
567            sender,
568            secondary_signer_addresses,
569            secondary_signers,
570        }
571    }
572
573    /// Creates a fee payer authenticator.
574    pub fn fee_payer(
575        sender: AccountAuthenticator,
576        secondary_signer_addresses: Vec<AccountAddress>,
577        secondary_signers: Vec<AccountAuthenticator>,
578        fee_payer_address: AccountAddress,
579        fee_payer_signer: AccountAuthenticator,
580    ) -> Self {
581        Self::FeePayer {
582            sender,
583            secondary_signer_addresses,
584            secondary_signers,
585            fee_payer_address,
586            fee_payer_signer,
587        }
588    }
589
590    /// Creates a single sender authenticator.
591    /// This is used for accounts with the unified key model (including multi-key accounts).
592    pub fn single_sender(sender: AccountAuthenticator) -> Self {
593        Self::SingleSender { sender }
594    }
595
596    /// Rewrites this transaction authenticator for `/transactions/simulate`.
597    ///
598    /// Delegates nested [`AccountAuthenticator`] values to
599    /// [`AccountAuthenticator::for_simulate_endpoint`]. Top-level legacy
600    /// [`TransactionAuthenticator::Ed25519`] / [`TransactionAuthenticator::MultiEd25519`]
601    /// variants keep their public keys but zero signature bytes (there is no
602    /// [`AccountAuthenticator::NoAccountAuthenticator`] field in those top-level
603    /// authenticator shapes).
604    #[must_use]
605    pub fn for_simulate_endpoint(self) -> Self {
606        match self {
607            Self::Ed25519 {
608                public_key,
609                signature: _,
610            } => Self::Ed25519 {
611                public_key,
612                signature: Ed25519Signature([0u8; 64]),
613            },
614            Self::MultiEd25519 {
615                public_key,
616                signature,
617            } => Self::MultiEd25519 {
618                public_key,
619                signature: zeroed_multi_ed25519_signature(&signature),
620            },
621            Self::MultiAgent {
622                sender,
623                secondary_signer_addresses,
624                secondary_signers,
625            } => {
626                let secondary: Vec<AccountAuthenticator> = secondary_signers
627                    .into_iter()
628                    .map(AccountAuthenticator::for_simulate_endpoint)
629                    .collect();
630                Self::multi_agent(
631                    sender.for_simulate_endpoint(),
632                    secondary_signer_addresses,
633                    secondary,
634                )
635            }
636            Self::FeePayer {
637                sender,
638                secondary_signer_addresses,
639                secondary_signers,
640                fee_payer_address,
641                fee_payer_signer,
642            } => {
643                let secondary: Vec<AccountAuthenticator> = secondary_signers
644                    .into_iter()
645                    .map(AccountAuthenticator::for_simulate_endpoint)
646                    .collect();
647                Self::fee_payer(
648                    sender.for_simulate_endpoint(),
649                    secondary_signer_addresses,
650                    secondary,
651                    fee_payer_address,
652                    fee_payer_signer.for_simulate_endpoint(),
653                )
654            }
655            Self::SingleSender { sender } => Self::single_sender(sender.for_simulate_endpoint()),
656        }
657    }
658}
659
660impl AccountAuthenticator {
661    /// Creates an Ed25519 account authenticator.
662    pub fn ed25519(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
663        Self::Ed25519 {
664            public_key: public_key.into(),
665            signature: signature.into(),
666        }
667    }
668    /// Creates a single-key account authenticator.
669    pub fn single_key(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
670        Self::SingleKey {
671            public_key,
672            signature,
673        }
674    }
675
676    /// Creates a multi-key account authenticator.
677    pub fn multi_key(public_key: Vec<u8>, signature: Vec<u8>) -> Self {
678        Self::MultiKey {
679            public_key,
680            signature,
681        }
682    }
683
684    /// Creates a no account authenticator.
685    pub fn no_account_authenticator() -> Self {
686        Self::NoAccountAuthenticator
687    }
688
689    /// Rewrites this authenticator for the Aptos `/transactions/simulate` endpoint.
690    ///
691    /// The fullnode rejects requests whose authenticators contain a cryptographically
692    /// **valid** signature (HTTP 400: "Simulated transactions must not have a valid
693    /// signature"). The SDK applies this transform automatically before simulate HTTP
694    /// calls so callers do not need to hand-replace authenticators.
695    ///
696    /// * [`SingleKey`](AccountAuthenticator::SingleKey)
697    ///   becomes [`AccountAuthenticator::NoAccountAuthenticator`], matching the common workaround for unified-key
698    ///   accounts.
699    /// * [`Ed25519`](AccountAuthenticator::Ed25519), [`MultiEd25519`](AccountAuthenticator::MultiEd25519),
700    ///   and [`MultiKey`](AccountAuthenticator::MultiKey) keep their public key material but replace
701    ///   only the signature bytes with zeros. For `MultiEd25519` / `MultiKey` the surrounding
702    ///   framing (signer count, per-signature variant tags/lengths, and the bitmap/`BitVec`) is
703    ///   preserved so the rewritten authenticator still deserializes into a valid
704    ///   `MultiEd25519Signature` / `MultiKeySignature` on the fullnode.
705    #[must_use]
706    pub fn for_simulate_endpoint(self) -> Self {
707        match self {
708            Self::NoAccountAuthenticator => Self::NoAccountAuthenticator,
709            Self::Ed25519 {
710                public_key,
711                signature: _,
712            } => Self::Ed25519 {
713                public_key,
714                signature: Ed25519Signature([0u8; 64]),
715            },
716            Self::MultiEd25519 {
717                public_key,
718                signature,
719            } => Self::MultiEd25519 {
720                public_key,
721                signature: zeroed_multi_ed25519_signature(&signature),
722            },
723            Self::SingleKey { .. } => Self::NoAccountAuthenticator,
724            Self::MultiKey {
725                public_key,
726                signature,
727            } => Self::MultiKey {
728                public_key,
729                signature: zeroed_multi_key_signature(&signature),
730            },
731        }
732    }
733
734    /// Verifies the authenticator against a signing message.
735    ///
736    /// # Errors
737    ///
738    /// Returns an error if the authenticator does not verify for the message.
739    pub fn verify(&self, message: &[u8]) -> crate::error::AptosResult<()> {
740        match self {
741            #[cfg(feature = "ed25519")]
742            Self::Ed25519 {
743                public_key,
744                signature,
745            } => {
746                let public_key = crate::crypto::Ed25519PublicKey::from_bytes(&public_key.0)?;
747                let signature = crate::crypto::Ed25519Signature::from_bytes(&signature.0)?;
748                public_key.verify(message, &signature)
749            }
750            #[cfg(not(feature = "ed25519"))]
751            Self::Ed25519 { .. } => Err(crate::error::AptosError::FeatureNotEnabled(
752                "Ed25519 verification".into(),
753            )),
754            #[cfg(feature = "ed25519")]
755            Self::MultiEd25519 {
756                public_key,
757                signature,
758            } => {
759                let public_key = crate::crypto::MultiEd25519PublicKey::from_bytes(public_key)?;
760                let signature = crate::crypto::MultiEd25519Signature::from_bytes(signature)?;
761                public_key.verify(message, &signature)
762            }
763            #[cfg(not(feature = "ed25519"))]
764            Self::MultiEd25519 { .. } => Err(crate::error::AptosError::FeatureNotEnabled(
765                "MultiEd25519 verification".into(),
766            )),
767            Self::SingleKey {
768                public_key,
769                signature,
770            } => {
771                let pk = crate::crypto::AnyPublicKey::from_bcs_bytes(public_key)?;
772                let sig = crate::crypto::AnySignature::from_bcs_bytes(signature)?;
773                pk.verify(message, &sig)
774            }
775            Self::MultiKey {
776                public_key,
777                signature,
778            } => {
779                let pk = crate::crypto::MultiKeyPublicKey::from_bytes(public_key)?;
780                let sig = crate::crypto::MultiKeySignature::from_bytes(signature)?;
781                pk.verify(message, &sig)
782            }
783            Self::NoAccountAuthenticator => Err(crate::error::AptosError::InvalidSignature(
784                "no account authenticator cannot be verified".into(),
785            )),
786        }
787    }
788
789    /// Returns the account address implied by this authenticator's public key material.
790    ///
791    /// # Errors
792    ///
793    /// Returns an error if the contained public key bytes cannot be parsed.
794    pub fn derived_address(&self) -> crate::error::AptosResult<AccountAddress> {
795        match self {
796            #[cfg(feature = "ed25519")]
797            Self::Ed25519 { public_key, .. } => {
798                let public_key = crate::crypto::Ed25519PublicKey::from_bytes(&public_key.0)?;
799                Ok(public_key.to_address())
800            }
801            #[cfg(not(feature = "ed25519"))]
802            Self::Ed25519 { .. } => Err(crate::error::AptosError::FeatureNotEnabled(
803                "Ed25519 address derivation".into(),
804            )),
805            #[cfg(feature = "ed25519")]
806            Self::MultiEd25519 { public_key, .. } => {
807                let public_key = crate::crypto::MultiEd25519PublicKey::from_bytes(public_key)?;
808                Ok(public_key.to_address())
809            }
810            #[cfg(not(feature = "ed25519"))]
811            Self::MultiEd25519 { .. } => Err(crate::error::AptosError::FeatureNotEnabled(
812                "MultiEd25519 address derivation".into(),
813            )),
814            Self::SingleKey { public_key, .. } => Ok(AccountAddress::new(
815                crate::crypto::derive_authentication_key(
816                    public_key,
817                    crate::crypto::SINGLE_KEY_SCHEME,
818                ),
819            )),
820            Self::MultiKey { public_key, .. } => {
821                let pk = crate::crypto::MultiKeyPublicKey::from_bytes(public_key)?;
822                Ok(pk.to_address())
823            }
824            Self::NoAccountAuthenticator => Err(crate::error::AptosError::InvalidSignature(
825                "no account authenticator has no derived address".into(),
826            )),
827        }
828    }
829}
830
831#[cfg(test)]
832mod tests {
833    use super::*;
834    use crate::crypto::{AnyPublicKey, AnySignature, MultiKeyPublicKey, MultiKeySignature};
835
836    #[test]
837    fn test_ed25519_authenticator() {
838        let mut pk = [0u8; 32];
839        pk[0..3].copy_from_slice(&[1, 2, 3]);
840        let mut sig = [0u8; 64];
841        sig[0..3].copy_from_slice(&[4, 5, 6]);
842
843        let auth = Ed25519Authenticator::new(pk.to_vec(), sig.to_vec());
844        let txn_auth: TransactionAuthenticator = auth.into();
845
846        match txn_auth {
847            TransactionAuthenticator::Ed25519 {
848                public_key,
849                signature,
850            } => {
851                assert_eq!(public_key.0[0..3], [1, 2, 3]);
852                assert_eq!(signature.0[0..3], [4, 5, 6]);
853            }
854            _ => panic!("wrong authenticator type"),
855        }
856    }
857
858    #[test]
859    fn test_multi_agent_authenticator() {
860        let sender = AccountAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
861        let auth = TransactionAuthenticator::multi_agent(sender, vec![], vec![]);
862
863        match auth {
864            TransactionAuthenticator::MultiAgent { .. } => {}
865            _ => panic!("wrong authenticator type"),
866        }
867    }
868
869    #[test]
870    fn test_ed25519_bcs_format() {
871        // Test that Ed25519 serializes WITH length prefixes (Aptos BCS format)
872        let auth = TransactionAuthenticator::Ed25519 {
873            public_key: Ed25519PublicKey([0xab; 32]),
874            signature: Ed25519Signature([0xcd; 64]),
875        };
876        let bcs = aptos_bcs::to_bytes(&auth).unwrap();
877
878        // Ed25519 variant should be index 0
879        assert_eq!(bcs[0], 0, "Ed25519 variant index should be 0");
880        // Next byte is length prefix for pubkey (32 = 0x20)
881        assert_eq!(bcs[1], 32, "Pubkey length prefix should be 32");
882        // Next 32 bytes should be the pubkey
883        assert_eq!(bcs[2], 0xab, "First pubkey byte should be 0xab");
884        // After pubkey (1 + 1 + 32 = 34), length prefix for signature (64 = 0x40)
885        assert_eq!(bcs[34], 64, "Signature length prefix should be 64");
886        // Signature starts at offset 35
887        assert_eq!(bcs[35], 0xcd, "First signature byte should be 0xcd");
888        // Total: 1 (variant) + 1 (pubkey len) + 32 (pubkey) + 1 (sig len) + 64 (sig) = 99
889        assert_eq!(bcs.len(), 99, "BCS length should be 99");
890    }
891
892    #[test]
893    fn test_ed25519_authenticator_into_account_authenticator() {
894        let auth = Ed25519Authenticator::new(vec![0xaa; 32], vec![0xbb; 64]);
895        let account_auth: AccountAuthenticator = auth.into();
896
897        match account_auth {
898            AccountAuthenticator::Ed25519 {
899                public_key,
900                signature,
901            } => {
902                assert_eq!(public_key.0[0], 0xaa);
903                assert_eq!(signature.0[0], 0xbb);
904            }
905            _ => panic!("Expected Ed25519 variant"),
906        }
907    }
908
909    #[test]
910    fn test_transaction_authenticator_ed25519() {
911        let auth = TransactionAuthenticator::ed25519(vec![0x11; 32], vec![0x22; 64]);
912        match auth {
913            TransactionAuthenticator::Ed25519 {
914                public_key,
915                signature,
916            } => {
917                assert_eq!(public_key.0[0], 0x11);
918                assert_eq!(signature.0[0], 0x22);
919            }
920            _ => panic!("Expected Ed25519 variant"),
921        }
922    }
923
924    #[test]
925    fn test_transaction_authenticator_multi_ed25519() {
926        let auth = TransactionAuthenticator::multi_ed25519(vec![0x33; 64], vec![0x44; 128]);
927        match auth {
928            TransactionAuthenticator::MultiEd25519 {
929                public_key,
930                signature,
931            } => {
932                assert_eq!(public_key.len(), 64);
933                assert_eq!(signature.len(), 128);
934            }
935            _ => panic!("Expected MultiEd25519 variant"),
936        }
937    }
938
939    #[test]
940    fn test_fee_payer_authenticator() {
941        let sender = AccountAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
942        let fee_payer = AccountAuthenticator::ed25519(vec![1; 32], vec![1; 64]);
943        let fee_payer_address = AccountAddress::from_hex("0x123").unwrap();
944
945        let auth = TransactionAuthenticator::fee_payer(
946            sender,
947            vec![],
948            vec![],
949            fee_payer_address,
950            fee_payer,
951        );
952
953        match auth {
954            TransactionAuthenticator::FeePayer {
955                fee_payer_address: addr,
956                ..
957            } => {
958                assert_eq!(addr, fee_payer_address);
959            }
960            _ => panic!("Expected FeePayer variant"),
961        }
962    }
963
964    #[test]
965    fn test_single_sender_authenticator() {
966        let sender = AccountAuthenticator::ed25519(vec![0x55; 32], vec![0x66; 64]);
967        let auth = TransactionAuthenticator::single_sender(sender);
968
969        match auth {
970            TransactionAuthenticator::SingleSender { sender } => match sender {
971                AccountAuthenticator::Ed25519 { public_key, .. } => {
972                    assert_eq!(public_key.0[0], 0x55);
973                }
974                _ => panic!("Expected Ed25519 sender"),
975            },
976            _ => panic!("Expected SingleSender variant"),
977        }
978    }
979
980    #[test]
981    fn test_account_authenticator_multi_key() {
982        let auth = AccountAuthenticator::multi_key(vec![0x77; 100], vec![0x88; 200]);
983        match auth {
984            AccountAuthenticator::MultiKey {
985                public_key,
986                signature,
987            } => {
988                assert_eq!(public_key.len(), 100);
989                assert_eq!(signature.len(), 200);
990            }
991            _ => panic!("Expected MultiKey variant"),
992        }
993    }
994
995    #[test]
996    fn test_ed25519_public_key_from_vec() {
997        let pk: Ed25519PublicKey = vec![0x12; 32].into();
998        assert_eq!(pk.0[0], 0x12);
999        assert_eq!(pk.0.len(), 32);
1000    }
1001
1002    #[test]
1003    fn test_ed25519_signature_from_vec() {
1004        let sig: Ed25519Signature = vec![0x34; 64].into();
1005        assert_eq!(sig.0[0], 0x34);
1006        assert_eq!(sig.0.len(), 64);
1007    }
1008
1009    #[test]
1010    fn test_ed25519_public_key_bcs_roundtrip() {
1011        let pk = Ed25519PublicKey([0xef; 32]);
1012        let serialized = aptos_bcs::to_bytes(&pk).unwrap();
1013        // Aptos BCS format: 1 byte length prefix (32) + 32 bytes = 33 bytes
1014        assert_eq!(serialized.len(), 33);
1015        assert_eq!(serialized[0], 32); // Length prefix
1016        let deserialized: Ed25519PublicKey = aptos_bcs::from_bytes(&serialized).unwrap();
1017        assert_eq!(pk, deserialized);
1018    }
1019
1020    #[test]
1021    fn test_ed25519_signature_bcs_roundtrip() {
1022        let sig = Ed25519Signature([0x99; 64]);
1023        let serialized = aptos_bcs::to_bytes(&sig).unwrap();
1024        let deserialized: Ed25519Signature = aptos_bcs::from_bytes(&serialized).unwrap();
1025        assert_eq!(sig, deserialized);
1026    }
1027
1028    #[test]
1029    fn test_multi_agent_with_secondary_signers() {
1030        let sender = AccountAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
1031        let secondary_signer1 = AccountAuthenticator::ed25519(vec![1; 32], vec![1; 64]);
1032        let secondary_signer2 = AccountAuthenticator::ed25519(vec![2; 32], vec![2; 64]);
1033        let addr1 = AccountAddress::from_hex("0x111").unwrap();
1034        let addr2 = AccountAddress::from_hex("0x222").unwrap();
1035
1036        let auth = TransactionAuthenticator::multi_agent(
1037            sender,
1038            vec![addr1, addr2],
1039            vec![secondary_signer1, secondary_signer2],
1040        );
1041
1042        match auth {
1043            TransactionAuthenticator::MultiAgent {
1044                secondary_signer_addresses,
1045                secondary_signers,
1046                ..
1047            } => {
1048                assert_eq!(secondary_signer_addresses.len(), 2);
1049                assert_eq!(secondary_signers.len(), 2);
1050            }
1051            _ => panic!("Expected MultiAgent variant"),
1052        }
1053    }
1054
1055    #[test]
1056    fn test_transaction_authenticator_bcs_roundtrip() {
1057        let auth = TransactionAuthenticator::Ed25519 {
1058            public_key: Ed25519PublicKey([0x11; 32]),
1059            signature: Ed25519Signature([0x22; 64]),
1060        };
1061
1062        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1063        let deserialized: TransactionAuthenticator = aptos_bcs::from_bytes(&serialized).unwrap();
1064
1065        assert_eq!(auth, deserialized);
1066    }
1067
1068    #[test]
1069    fn test_account_authenticator_bcs_roundtrip() {
1070        let auth = AccountAuthenticator::Ed25519 {
1071            public_key: Ed25519PublicKey([0x33; 32]),
1072            signature: Ed25519Signature([0x44; 64]),
1073        };
1074
1075        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1076        let deserialized: AccountAuthenticator = aptos_bcs::from_bytes(&serialized).unwrap();
1077
1078        assert_eq!(auth, deserialized);
1079    }
1080
1081    #[test]
1082    fn test_account_authenticator_single_key() {
1083        let auth = AccountAuthenticator::single_key(vec![0x55; 33], vec![0x66; 65]);
1084        match auth {
1085            AccountAuthenticator::SingleKey {
1086                public_key,
1087                signature,
1088            } => {
1089                assert_eq!(public_key.len(), 33);
1090                assert_eq!(signature.len(), 65);
1091            }
1092            _ => panic!("Expected SingleKey variant"),
1093        }
1094    }
1095
1096    #[test]
1097    fn test_account_authenticator_single_key_bcs_wire_format() {
1098        // The on-chain `AccountAuthenticator::SingleKey { authenticator: SingleKeyAuthenticator }`
1099        // BCS encoding is:
1100        //   * variant tag (ULEB128 of 2) -> 1 byte
1101        //   * BCS(SingleKeyAuthenticator) = BCS(AnyPublicKey) || BCS(AnySignature)
1102        //
1103        // The inner public_key/signature byte runs already start with their own
1104        // enum/struct tags, so they must be emitted *without* any additional
1105        // length prefix. Verify this by hand-building the expected output.
1106        let pk = vec![0x77; 33]; // simulated AnyPublicKey bytes
1107        let sig = vec![0x88; 65]; // simulated AnySignature bytes
1108
1109        let auth = AccountAuthenticator::SingleKey {
1110            public_key: pk.clone(),
1111            signature: sig.clone(),
1112        };
1113
1114        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1115        let mut expected = Vec::new();
1116        expected.push(2u8); // variant tag
1117        expected.extend_from_slice(&pk);
1118        expected.extend_from_slice(&sig);
1119        assert_eq!(
1120            serialized, expected,
1121            "SingleKey wire format must be variant tag + raw pubkey bytes + raw signature bytes \
1122             (no inner length prefixes)"
1123        );
1124    }
1125
1126    #[cfg(feature = "ed25519")]
1127    #[test]
1128    fn test_account_authenticator_single_key_verify_and_derived_address() {
1129        use crate::crypto::{Ed25519PrivateKey, SINGLE_KEY_SCHEME, derive_authentication_key};
1130
1131        let private_key = Ed25519PrivateKey::generate();
1132        let message = b"single-key verify test";
1133        let public_key = crate::crypto::AnyPublicKey::ed25519(&private_key.public_key());
1134        let signature = crate::crypto::AnySignature::ed25519(&private_key.sign(message));
1135        let auth =
1136            AccountAuthenticator::single_key(public_key.to_bcs_bytes(), signature.to_bcs_bytes());
1137
1138        auth.verify(message).unwrap();
1139        let expected = AccountAddress::new(derive_authentication_key(
1140            &public_key.to_bcs_bytes(),
1141            SINGLE_KEY_SCHEME,
1142        ));
1143        assert_eq!(auth.derived_address().unwrap(), expected);
1144    }
1145
1146    #[cfg(feature = "ed25519")]
1147    #[test]
1148    fn test_account_authenticator_multi_ed25519_verify_and_derived_address() {
1149        use crate::account::{Account, MultiEd25519Account};
1150        use crate::crypto::Ed25519PrivateKey;
1151
1152        let account = MultiEd25519Account::new(
1153            vec![Ed25519PrivateKey::generate(), Ed25519PrivateKey::generate()],
1154            2,
1155        )
1156        .unwrap();
1157        let message = b"multi-ed25519 verify test";
1158        let auth = AccountAuthenticator::MultiEd25519 {
1159            public_key: account.public_key_bytes(),
1160            signature: account.sign(message).unwrap().to_bytes(),
1161        };
1162
1163        auth.verify(message).unwrap();
1164        assert_eq!(auth.derived_address().unwrap(), account.address());
1165    }
1166
1167    #[test]
1168    fn test_no_account_authenticator() {
1169        let auth = AccountAuthenticator::no_account_authenticator();
1170        match auth {
1171            AccountAuthenticator::NoAccountAuthenticator => {}
1172            _ => panic!("Expected NoAccountAuthenticator variant"),
1173        }
1174    }
1175
1176    #[cfg(feature = "ed25519")]
1177    #[test]
1178    fn test_account_authenticator_for_simulate_endpoint_single_key_to_no_account() {
1179        let auth = AccountAuthenticator::single_key(vec![0x01, 0x02], vec![0x03, 0x04]);
1180        let sanitized = auth.for_simulate_endpoint();
1181        assert!(matches!(
1182            sanitized,
1183            AccountAuthenticator::NoAccountAuthenticator
1184        ));
1185    }
1186
1187    #[cfg(feature = "ed25519")]
1188    #[test]
1189    fn test_transaction_authenticator_for_simulate_endpoint_single_sender_strips_single_key() {
1190        let sender = AccountAuthenticator::single_key(vec![0x05, 0x06], vec![0x07, 0x08]);
1191        let auth = TransactionAuthenticator::single_sender(sender);
1192        let sanitized = auth.for_simulate_endpoint();
1193        assert!(matches!(
1194            sanitized,
1195            TransactionAuthenticator::SingleSender { ref sender }
1196                if matches!(sender, AccountAuthenticator::NoAccountAuthenticator)
1197        ));
1198    }
1199
1200    #[cfg(feature = "ed25519")]
1201    #[test]
1202    fn test_transaction_authenticator_for_simulate_endpoint_ed25519_zeros_sig() {
1203        let auth = TransactionAuthenticator::Ed25519 {
1204            public_key: Ed25519PublicKey([7u8; 32]),
1205            signature: Ed25519Signature([9u8; 64]),
1206        };
1207        let sanitized = auth.for_simulate_endpoint();
1208        match sanitized {
1209            TransactionAuthenticator::Ed25519 { signature, .. } => {
1210                assert_eq!(signature.0, [0u8; 64]);
1211            }
1212            _ => panic!("expected Ed25519"),
1213        }
1214    }
1215
1216    #[test]
1217    fn test_no_account_authenticator_verify_and_derived_address_errors() {
1218        let auth = AccountAuthenticator::NoAccountAuthenticator;
1219        assert!(auth.verify(b"no-auth").is_err());
1220        assert!(auth.derived_address().is_err());
1221    }
1222
1223    #[test]
1224    fn test_no_account_authenticator_bcs_roundtrip() {
1225        let auth = AccountAuthenticator::NoAccountAuthenticator;
1226
1227        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1228        // NoAccountAuthenticator should be variant index 4
1229        assert_eq!(
1230            serialized[0], 4,
1231            "NoAccountAuthenticator variant index should be 4"
1232        );
1233        // It should be just the variant index, no payload
1234        assert_eq!(
1235            serialized.len(),
1236            1,
1237            "NoAccountAuthenticator should be 1 byte"
1238        );
1239        let deserialized: AccountAuthenticator = aptos_bcs::from_bytes(&serialized).unwrap();
1240        assert_eq!(auth, deserialized);
1241    }
1242
1243    #[test]
1244    fn test_single_sender_with_single_key() {
1245        let sender = AccountAuthenticator::single_key(vec![0x99; 33], vec![0xaa; 65]);
1246        let auth = TransactionAuthenticator::single_sender(sender);
1247
1248        match auth {
1249            TransactionAuthenticator::SingleSender { sender } => match sender {
1250                AccountAuthenticator::SingleKey { public_key, .. } => {
1251                    assert_eq!(public_key.len(), 33);
1252                }
1253                _ => panic!("Expected SingleKey sender"),
1254            },
1255            _ => panic!("Expected SingleSender variant"),
1256        }
1257    }
1258
1259    #[test]
1260    fn test_account_authenticator_variant_indices() {
1261        // Verify all variant indices match Aptos core
1262        let ed25519 = AccountAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
1263        let multi_ed25519 = AccountAuthenticator::MultiEd25519 {
1264            public_key: vec![0; 64],
1265            signature: vec![0; 128],
1266        };
1267        let single_key = AccountAuthenticator::single_key(vec![0; 33], vec![0; 65]);
1268        let multi_key = AccountAuthenticator::multi_key(vec![0; 100], vec![0; 200]);
1269        let no_account = AccountAuthenticator::no_account_authenticator();
1270
1271        assert_eq!(aptos_bcs::to_bytes(&ed25519).unwrap()[0], 0, "Ed25519 = 0");
1272        assert_eq!(
1273            aptos_bcs::to_bytes(&multi_ed25519).unwrap()[0],
1274            1,
1275            "MultiEd25519 = 1"
1276        );
1277        assert_eq!(
1278            aptos_bcs::to_bytes(&single_key).unwrap()[0],
1279            2,
1280            "SingleKey = 2"
1281        );
1282        assert_eq!(
1283            aptos_bcs::to_bytes(&multi_key).unwrap()[0],
1284            3,
1285            "MultiKey = 3"
1286        );
1287        assert_eq!(
1288            aptos_bcs::to_bytes(&no_account).unwrap()[0],
1289            4,
1290            "NoAccountAuthenticator = 4"
1291        );
1292    }
1293
1294    #[test]
1295    fn test_ed25519_public_key_try_from_bytes_valid() {
1296        let bytes = vec![0x12; 32];
1297        let pk = Ed25519PublicKey::try_from_bytes(&bytes).unwrap();
1298        assert_eq!(pk.0[0], 0x12);
1299    }
1300
1301    #[test]
1302    fn test_ed25519_public_key_try_from_bytes_invalid_length() {
1303        let bytes = vec![0x12; 16]; // Wrong length
1304        let result = Ed25519PublicKey::try_from_bytes(&bytes);
1305        assert!(result.is_err());
1306    }
1307
1308    #[test]
1309    fn test_ed25519_signature_try_from_bytes_valid() {
1310        let bytes = vec![0x34; 64];
1311        let sig = Ed25519Signature::try_from_bytes(&bytes).unwrap();
1312        assert_eq!(sig.0[0], 0x34);
1313    }
1314
1315    #[test]
1316    fn test_ed25519_signature_try_from_bytes_invalid_length() {
1317        let bytes = vec![0x34; 32]; // Wrong length
1318        let result = Ed25519Signature::try_from_bytes(&bytes);
1319        assert!(result.is_err());
1320    }
1321
1322    #[test]
1323    fn test_transaction_authenticator_variant_indices() {
1324        // Verify transaction authenticator variant indices
1325        let ed25519 = TransactionAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
1326        let multi_ed25519 = TransactionAuthenticator::multi_ed25519(vec![0; 64], vec![0; 128]);
1327        let sender = AccountAuthenticator::ed25519(vec![0; 32], vec![0; 64]);
1328        let multi_agent = TransactionAuthenticator::multi_agent(sender.clone(), vec![], vec![]);
1329        let fee_payer = TransactionAuthenticator::fee_payer(
1330            sender.clone(),
1331            vec![],
1332            vec![],
1333            AccountAddress::ONE,
1334            sender.clone(),
1335        );
1336        let single_sender = TransactionAuthenticator::single_sender(sender);
1337
1338        assert_eq!(aptos_bcs::to_bytes(&ed25519).unwrap()[0], 0, "Ed25519 = 0");
1339        assert_eq!(
1340            aptos_bcs::to_bytes(&multi_ed25519).unwrap()[0],
1341            1,
1342            "MultiEd25519 = 1"
1343        );
1344        assert_eq!(
1345            aptos_bcs::to_bytes(&multi_agent).unwrap()[0],
1346            2,
1347            "MultiAgent = 2"
1348        );
1349        assert_eq!(
1350            aptos_bcs::to_bytes(&fee_payer).unwrap()[0],
1351            3,
1352            "FeePayer = 3"
1353        );
1354        assert_eq!(
1355            aptos_bcs::to_bytes(&single_sender).unwrap()[0],
1356            4,
1357            "SingleSender = 4"
1358        );
1359    }
1360
1361    #[test]
1362    fn test_single_key_single_sender_bcs_wire_format() {
1363        // Pin the byte-for-byte wire layout of
1364        // `TransactionAuthenticator::SingleSender(AccountAuthenticator::SingleKey)`
1365        // so that a future regression in the hand-rolled Serialize impl is
1366        // caught at unit-test time (rather than at submission time on the
1367        // chain). The inner AnyPublicKey / AnySignature payloads must be
1368        // emitted *inline* after the variant tags -- no outer length prefixes.
1369        let mut pk = vec![0u8; 67];
1370        pk[0] = 0x02; // AnyPublicKey::Secp256r1Ecdsa variant
1371        pk[1] = 65; // ULEB128(65)
1372        pk[2] = 0x04; // SEC1 uncompressed marker
1373        let mut sig = vec![0u8; 66];
1374        sig[0] = 0x02; // AnySignature::WebAuthn variant
1375        sig[1] = 64; // ULEB128(64)
1376
1377        let auth = AccountAuthenticator::single_key(pk.clone(), sig.clone());
1378        let bytes = aptos_bcs::to_bytes(&auth).unwrap();
1379        let mut expected_inner = Vec::new();
1380        expected_inner.push(2u8); // AccountAuthenticator::SingleKey variant tag
1381        expected_inner.extend_from_slice(&pk); // AnyPublicKey inline (no length prefix)
1382        expected_inner.extend_from_slice(&sig); // AnySignature inline (no length prefix)
1383        assert_eq!(bytes, expected_inner);
1384
1385        let txn = TransactionAuthenticator::single_sender(auth);
1386        let bytes = aptos_bcs::to_bytes(&txn).unwrap();
1387        let mut expected_outer = Vec::new();
1388        expected_outer.push(4u8); // TransactionAuthenticator::SingleSender variant tag
1389        expected_outer.extend_from_slice(&expected_inner);
1390        assert_eq!(bytes, expected_outer);
1391    }
1392
1393    #[test]
1394    fn test_multi_key_authenticator_bcs_wire_format() {
1395        // Same logic as test_account_authenticator_single_key_bcs_wire_format, but for
1396        // MultiKey. The on-chain `AccountAuthenticator::MultiKey { authenticator: MultiKeyAuthenticator }`
1397        // BCS encoding is:
1398        //   * variant tag (ULEB128 of 3) -> 1 byte
1399        //   * BCS(MultiKeyPublicKey) || BCS(MultiKeySignature)
1400        // Each inner blob already carries its own structural framing.
1401        let pk = vec![0xaa; 100];
1402        let sig = vec![0xbb; 200];
1403        let auth = AccountAuthenticator::multi_key(pk.clone(), sig.clone());
1404
1405        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1406        let mut expected = Vec::new();
1407        expected.push(3u8); // variant tag
1408        expected.extend_from_slice(&pk);
1409        expected.extend_from_slice(&sig);
1410        assert_eq!(
1411            serialized, expected,
1412            "MultiKey wire format must be variant tag + raw pubkey bytes + raw signature bytes"
1413        );
1414    }
1415
1416    #[test]
1417    fn test_multi_key_authenticator_bcs_rejects_keyless_public_key() {
1418        let mk_pk = MultiKeyPublicKey::new(
1419            vec![AnyPublicKey::new(
1420                crate::crypto::AnyPublicKeyVariant::Keyless,
1421                vec![],
1422            )],
1423            1,
1424        )
1425        .unwrap();
1426        let mk_sig = MultiKeySignature::new(vec![(
1427            0,
1428            AnySignature::new(crate::crypto::AnyPublicKeyVariant::Ed25519, vec![0x66; 64]),
1429        )])
1430        .unwrap();
1431        let auth = AccountAuthenticator::multi_key(mk_pk.to_bytes(), mk_sig.to_bytes());
1432
1433        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1434        let result: Result<AccountAuthenticator, _> = aptos_bcs::from_bytes(&serialized);
1435        assert!(result.is_err());
1436    }
1437
1438    #[test]
1439    fn test_multi_ed25519_authenticator_bcs_roundtrip() {
1440        let auth = AccountAuthenticator::MultiEd25519 {
1441            public_key: vec![0xcc; 64],
1442            signature: vec![0xdd; 128],
1443        };
1444
1445        let serialized = aptos_bcs::to_bytes(&auth).unwrap();
1446        // MultiEd25519 should be variant index 1
1447        assert_eq!(serialized[0], 1, "MultiEd25519 variant index should be 1");
1448        let deserialized: AccountAuthenticator = aptos_bcs::from_bytes(&serialized).unwrap();
1449        assert_eq!(auth, deserialized);
1450    }
1451
1452    #[test]
1453    fn test_ed25519_public_key_deserialize_invalid_length() {
1454        // Serialize with wrong length (use 16 bytes instead of 32)
1455        let mut bytes = vec![16u8]; // Length prefix
1456        bytes.extend_from_slice(&[0xab; 16]); // Only 16 bytes
1457        let result: Result<Ed25519PublicKey, _> = aptos_bcs::from_bytes(&bytes);
1458        assert!(result.is_err());
1459    }
1460
1461    #[test]
1462    fn test_ed25519_signature_deserialize_invalid_length() {
1463        // Serialize with wrong length (use 32 bytes instead of 64)
1464        let mut bytes = vec![32u8]; // Length prefix
1465        bytes.extend_from_slice(&[0xab; 32]); // Only 32 bytes
1466        let result: Result<Ed25519Signature, _> = aptos_bcs::from_bytes(&bytes);
1467        assert!(result.is_err());
1468    }
1469
1470    #[cfg(feature = "ed25519")]
1471    #[test]
1472    fn test_account_authenticator_for_simulate_endpoint_multi_ed25519_stays_valid() {
1473        use crate::account::{Account, MultiEd25519Account};
1474        use crate::crypto::{Ed25519PrivateKey, MultiEd25519Signature};
1475
1476        let account = MultiEd25519Account::new(
1477            vec![
1478                Ed25519PrivateKey::generate(),
1479                Ed25519PrivateKey::generate(),
1480                Ed25519PrivateKey::generate(),
1481            ],
1482            2,
1483        )
1484        .unwrap();
1485        let message = b"multi-ed25519 simulate test";
1486        let original_sig_bytes = account.sign(message).unwrap().to_bytes();
1487        let original = MultiEd25519Signature::from_bytes(&original_sig_bytes).unwrap();
1488
1489        let auth = AccountAuthenticator::MultiEd25519 {
1490            public_key: account.public_key_bytes(),
1491            signature: original_sig_bytes.clone(),
1492        };
1493
1494        let AccountAuthenticator::MultiEd25519 { signature, .. } = auth.for_simulate_endpoint()
1495        else {
1496            panic!("expected MultiEd25519 after simulate rewrite");
1497        };
1498
1499        // Same total blob length as the input.
1500        assert_eq!(signature.len(), original_sig_bytes.len());
1501
1502        // The rewritten blob must still deserialize into a valid
1503        // MultiEd25519Signature with the SAME signer count and SAME bitmap.
1504        let rewritten = MultiEd25519Signature::from_bytes(&signature).unwrap();
1505        assert_eq!(rewritten.num_signatures(), original.num_signatures());
1506        assert_eq!(rewritten.bitmap(), original.bitmap());
1507
1508        // Every signature scalar byte is zero (only the 4-byte bitmap survives).
1509        assert!(signature[..signature.len() - 4].iter().all(|b| *b == 0));
1510        for (_, sig) in rewritten.signatures() {
1511            let sig_bytes = sig.to_bytes();
1512            assert_eq!(sig_bytes.len(), 64);
1513            assert!(sig_bytes.iter().all(|b| *b == 0));
1514        }
1515    }
1516
1517    #[cfg(feature = "ed25519")]
1518    #[test]
1519    fn test_account_authenticator_for_simulate_endpoint_multi_key_stays_valid() {
1520        use crate::crypto::{
1521            AnyPublicKey, AnySignature, Ed25519PrivateKey, MultiKeyPublicKey, MultiKeySignature,
1522        };
1523
1524        let sk0 = Ed25519PrivateKey::generate();
1525        let sk1 = Ed25519PrivateKey::generate();
1526        let sk2 = Ed25519PrivateKey::generate();
1527        let pk = MultiKeyPublicKey::new(
1528            vec![
1529                AnyPublicKey::ed25519(&sk0.public_key()),
1530                AnyPublicKey::ed25519(&sk1.public_key()),
1531                AnyPublicKey::ed25519(&sk2.public_key()),
1532            ],
1533            2,
1534        )
1535        .unwrap();
1536        let message = b"multi-key simulate test";
1537        let original = MultiKeySignature::new(vec![
1538            (0, AnySignature::ed25519(&sk0.sign(message))),
1539            (2, AnySignature::ed25519(&sk2.sign(message))),
1540        ])
1541        .unwrap();
1542        let original_sig_bytes = original.to_bytes();
1543
1544        let auth = AccountAuthenticator::multi_key(pk.to_bytes(), original_sig_bytes.clone());
1545        let AccountAuthenticator::MultiKey { signature, .. } = auth.for_simulate_endpoint() else {
1546            panic!("expected MultiKey after simulate rewrite");
1547        };
1548
1549        // Same total blob length as the input.
1550        assert_eq!(signature.len(), original_sig_bytes.len());
1551
1552        // The rewritten blob must still deserialize into a valid MultiKeySignature
1553        // with the SAME signer count and SAME bitmap, but zeroed signature bytes.
1554        let rewritten = MultiKeySignature::from_bytes(&signature).unwrap();
1555        assert_eq!(rewritten.num_signatures(), original.num_signatures());
1556        assert_eq!(rewritten.bitmap(), original.bitmap());
1557        for ((idx_r, sig_r), (idx_o, sig_o)) in
1558            rewritten.signatures().iter().zip(original.signatures())
1559        {
1560            assert_eq!(idx_r, idx_o);
1561            assert_eq!(sig_r.variant, sig_o.variant);
1562            assert_eq!(sig_r.bytes.len(), sig_o.bytes.len());
1563            assert!(sig_r.bytes.iter().all(|b| *b == 0));
1564        }
1565    }
1566
1567    #[test]
1568    fn test_zeroed_multi_ed25519_signature_wire_layout_pinned() {
1569        // Fixed input: two 64-byte signatures for signers {0, 2}. Aptos MSB-first
1570        // bitmap for {0, 2} is 0b1010_0000 in byte 0.
1571        let mut input = Vec::new();
1572        input.extend_from_slice(&[0x11u8; 64]);
1573        input.extend_from_slice(&[0x22u8; 64]);
1574        input.extend_from_slice(&[0b1010_0000, 0x00, 0x00, 0x00]);
1575
1576        let out = zeroed_multi_ed25519_signature(&input);
1577
1578        // Expected: all 128 signature bytes zeroed, 4-byte bitmap preserved.
1579        let mut expected = vec![0u8; 128];
1580        expected.extend_from_slice(&[0b1010_0000, 0x00, 0x00, 0x00]);
1581        assert_eq!(out, expected);
1582    }
1583
1584    #[test]
1585    fn test_zeroed_multi_key_signature_wire_layout_pinned() {
1586        // Fixed input: two Ed25519 AnySignatures for signers {0, 1}.
1587        // Layout: ULEB(num_sigs=2) || (0x00 0x40 payload)*2 || ULEB(4) || bitmap.
1588        // MSB-first bitmap for {0, 1} is 0b1100_0000.
1589        let mut input = vec![0x02u8]; // num_sigs = 2
1590        input.extend_from_slice(&[0x00, 0x40]); // Ed25519 variant + ULEB128(64)
1591        input.extend_from_slice(&[0xaa; 64]);
1592        input.extend_from_slice(&[0x00, 0x40]);
1593        input.extend_from_slice(&[0xbb; 64]);
1594        input.push(0x04); // BCS BitVec length prefix
1595        input.extend_from_slice(&[0b1100_0000, 0x00, 0x00, 0x00]);
1596
1597        let out = zeroed_multi_key_signature(&input);
1598
1599        let mut expected = vec![0x02u8];
1600        expected.extend_from_slice(&[0x00, 0x40]);
1601        expected.extend_from_slice(&[0u8; 64]);
1602        expected.extend_from_slice(&[0x00, 0x40]);
1603        expected.extend_from_slice(&[0u8; 64]);
1604        expected.push(0x04);
1605        expected.extend_from_slice(&[0b1100_0000, 0x00, 0x00, 0x00]);
1606        assert_eq!(
1607            out, expected,
1608            "simulate-rewritten MultiKey signature wire layout drifted"
1609        );
1610
1611        // And it still deserializes into a valid MultiKeySignature.
1612        let parsed = crate::crypto::MultiKeySignature::from_bytes(&out).unwrap();
1613        assert_eq!(parsed.num_signatures(), 2);
1614        assert_eq!(parsed.bitmap(), &[0b1100_0000, 0x00, 0x00, 0x00]);
1615    }
1616}