1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use pyo3::prelude::*;
use solana_sdk::signer::{presigner::Presigner as PresignerOriginal, Signer as SignerTrait};
use solders_macros::{pyhash, richcmp_signer};
use solders_pubkey::Pubkey;
use solders_signature::Signature;

use solders_traits::{
    handle_py_err, impl_signer_hash, RichcmpSigner, SignerTraitWrapper, ToSignerOriginal,
};
use solders_traits_core::{impl_display, PyHash};

#[derive(Clone, Debug, Default, PartialEq)]
#[pyclass(module = "solders.presigner", subclass)]
/// A signer that represents a :class:`~solders.signature.Signature` that has been
/// constructed externally. Performs a signature verification against the
/// expected message upon ``sign()`` requests to affirm its relationship to
/// the ``message`` bytes.
///
/// Args:
///     pubkey (Pubkey): The pubkey of the signer.
///     signature (Signature): The signature created by signing the message.
///     
pub struct Presigner(pub PresignerOriginal);

#[pyhash]
#[richcmp_signer]
#[pymethods]
impl Presigner {
    #[new]
    pub fn new(pubkey: &Pubkey, signature: &Signature) -> Self {
        PresignerOriginal::new(pubkey.as_ref(), signature.as_ref()).into()
    }

    #[pyo3(name = "pubkey")]
    /// Get this signer's :class:`~solders.pubkey.Pubkey`.
    ///
    /// Returns:
    ///     Pubkey: The pubkey of the presigner.
    ///
    /// Example:
    ///     >>> from solders.keypair import Keypair
    ///     >>> from solders.pubkey import Pubkey
    ///     >>> seed_bytes = bytes([0] * 32)
    ///     >>> pubkey_bytes = b";j'\xbc\xce\xb6\xa4-b\xa3\xa8\xd0*o\rse2\x15w\x1d\xe2C\xa6:\xc0H\xa1\x8bY\xda)"
    ///     >>> kp = Keypair.from_bytes(seed_bytes + pubkey_bytes)
    ///     >>> assert kp.pubkey() == Pubkey(pubkey_bytes)
    ///
    pub fn py_pubkey(&self) -> Pubkey {
        self.pubkey().into()
    }

    #[pyo3(name = "sign_message")]
    /// Verifies the signature of the presigner and returns it if valid.
    ///
    /// Returns:
    ///     Signature: The signature assigned to this object.
    ///
    /// Raises:
    ///     SignerError: if the signature is invalid.
    ///
    /// Example:
    ///
    ///     >>> from solders.keypair import Keypair
    ///     >>> from solders.presigner import Presigner
    ///     >>> keypair = Keypair.from_seed(bytes([0] * 32))
    ///     >>> pubkey = keypair.pubkey()
    ///     >>> data = bytes([1])
    ///     >>> sig = keypair.sign_message(data)
    ///     >>> presigner = Presigner(pubkey, sig)
    ///     >>> assert presigner.pubkey() == pubkey
    ///     >>> assert presigner.sign_message(data) == sig
    ///
    pub fn py_sign_message(&self, message: &[u8]) -> PyResult<Signature> {
        handle_py_err(self.try_sign_message(message))
    }

    #[staticmethod]
    #[pyo3(name = "default")]
    /// Create a new default presigner.
    ///
    /// Returns:
    ///     Presigner: The default presigner.
    ///
    pub fn new_default() -> Self {
        Self::default()
    }

    fn __repr__(&self) -> String {
        format!("{self:#?}")
    }
}

impl_display!(Presigner);
impl_signer_hash!(Presigner);
impl PyHash for Presigner {}

impl From<PresignerOriginal> for Presigner {
    fn from(signer: PresignerOriginal) -> Self {
        Self(signer)
    }
}

impl ToSignerOriginal for Presigner {
    fn to_inner(&self) -> Box<dyn SignerTrait> {
        Box::new(self.0.clone())
    }
}

impl SignerTraitWrapper for Presigner {}
impl RichcmpSigner for Presigner {}