Skip to main content

nym_contracts_common/signing/
mod.rs

1// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use cosmwasm_std::{Addr, Coin, MessageInfo, StdResult, from_json, to_json_vec};
5use schemars::JsonSchema;
6use serde::de::DeserializeOwned;
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
8use std::fmt::{Display, Formatter};
9use std::str::FromStr;
10pub use verifier::Verifier;
11
12pub mod verifier;
13
14pub type Nonce = u32;
15
16// define this type explicitly for [hopefully] better usability
17// (so you wouldn't need to worry about whether you should use bytes, bs58, etc.)
18#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)]
19pub struct MessageSignature(Vec<u8>);
20
21impl MessageSignature {
22    pub fn as_bs58_string(&self) -> String {
23        bs58::encode(&self.0).into_string()
24    }
25}
26
27impl<'a> From<&'a [u8]> for MessageSignature {
28    fn from(value: &'a [u8]) -> Self {
29        MessageSignature(value.to_vec())
30    }
31}
32
33impl From<Vec<u8>> for MessageSignature {
34    fn from(value: Vec<u8>) -> Self {
35        MessageSignature(value)
36    }
37}
38
39impl<'a> TryFrom<&'a str> for MessageSignature {
40    type Error = bs58::decode::Error;
41
42    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
43        Ok(MessageSignature(bs58::decode(value).into_vec()?))
44    }
45}
46
47impl TryFrom<String> for MessageSignature {
48    type Error = bs58::decode::Error;
49
50    fn try_from(value: String) -> Result<Self, Self::Error> {
51        Self::try_from(value.as_str())
52    }
53}
54
55impl AsRef<[u8]> for MessageSignature {
56    #[inline]
57    fn as_ref(&self) -> &[u8] {
58        &self.0
59    }
60}
61
62impl<'de> Deserialize<'de> for MessageSignature {
63    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64    where
65        D: Deserializer<'de>,
66    {
67        let inner = String::deserialize(deserializer)?;
68        let bytes = bs58::decode(inner).into_vec().map_err(de::Error::custom)?;
69        Ok(MessageSignature(bytes))
70    }
71}
72
73impl Serialize for MessageSignature {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: Serializer,
77    {
78        let bs58_encoded = self.as_bs58_string();
79        bs58_encoded.serialize(serializer)
80    }
81}
82
83impl Display for MessageSignature {
84    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85        write!(f, "{}", self.as_bs58_string())
86    }
87}
88
89impl FromStr for MessageSignature {
90    type Err = bs58::decode::Error;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        Self::try_from(s)
94    }
95}
96
97pub trait SigningPurpose {
98    fn message_type() -> MessageType;
99}
100
101#[derive(Serialize, Deserialize)]
102#[serde(transparent)]
103pub struct MessageType(String);
104
105impl MessageType {
106    pub fn new<S: Into<String>>(typ: S) -> Self {
107        MessageType(typ.into())
108    }
109}
110
111impl<T> From<T> for MessageType
112where
113    T: ToString,
114{
115    fn from(value: T) -> Self {
116        MessageType(value.to_string())
117    }
118}
119
120#[derive(Default, Debug, Serialize, Deserialize, Copy, Clone)]
121#[serde(rename_all = "lowercase")]
122pub enum SigningAlgorithm {
123    #[default]
124    Ed25519,
125    Secp256k1,
126}
127
128impl SigningAlgorithm {
129    pub fn is_ed25519(&self) -> bool {
130        matches!(self, SigningAlgorithm::Ed25519)
131    }
132}
133
134// TODO: maybe move this one to repo-wide common?
135// TODO: should it perhaps also include the public key itself?
136#[derive(Serialize, Deserialize)]
137pub struct SignableMessage<T> {
138    pub nonce: u32,
139    pub algorithm: SigningAlgorithm,
140    pub message_type: MessageType,
141
142    pub content: T,
143}
144
145impl<T> SignableMessage<T>
146where
147    T: SigningPurpose,
148{
149    pub fn new(nonce: u32, content: T) -> Self {
150        SignableMessage {
151            nonce,
152            algorithm: SigningAlgorithm::Ed25519,
153            message_type: T::message_type(),
154            content,
155        }
156    }
157
158    pub fn with_signing_algorithm(mut self, algorithm: SigningAlgorithm) -> Self {
159        self.algorithm = algorithm;
160        self
161    }
162
163    pub fn to_plaintext(&self) -> StdResult<Vec<u8>>
164    where
165        T: Serialize,
166    {
167        to_json_vec(self)
168    }
169
170    pub fn to_sha256_plaintext_digest(&self) -> StdResult<Vec<u8>>
171    where
172        T: Serialize,
173    {
174        unimplemented!()
175    }
176
177    pub fn to_json_string(&self) -> StdResult<String>
178    where
179        T: Serialize,
180    {
181        // if you look into implementation of `serde_json_wasm::to_string` this [i.e. the String conversion]
182        // CAN'T fail, but let's avoid this unnecessary unwrap either way
183        self.to_plaintext()
184            .map(|s| String::from_utf8(s).unwrap_or(String::from("SERIALIZATION FAILURE")))
185    }
186
187    pub fn to_base58_string(&self) -> StdResult<String>
188    where
189        T: Serialize,
190    {
191        self.to_plaintext().map(|s| bs58::encode(s).into_string())
192    }
193
194    pub fn try_from_bytes(bytes: &[u8]) -> StdResult<SignableMessage<T>>
195    where
196        T: DeserializeOwned,
197    {
198        from_json(bytes)
199    }
200
201    pub fn try_from_string(raw: &str) -> StdResult<SignableMessage<T>>
202    where
203        T: DeserializeOwned,
204    {
205        Self::try_from_bytes(raw.as_bytes())
206    }
207
208    pub fn try_from_base58_string(raw: &str) -> bs58::decode::Result<StdResult<SignableMessage<T>>>
209    where
210        T: DeserializeOwned,
211    {
212        bs58::decode(raw)
213            .into_vec()
214            .map(|d| Self::try_from_bytes(&d))
215    }
216}
217
218#[derive(Serialize)]
219pub struct ContractMessageContent<T> {
220    pub sender: Addr,
221    pub funds: Vec<Coin>,
222    pub data: T,
223}
224
225impl<T> SigningPurpose for ContractMessageContent<T>
226where
227    T: SigningPurpose,
228{
229    fn message_type() -> MessageType {
230        T::message_type()
231    }
232}
233
234impl<T> ContractMessageContent<T> {
235    pub fn new(sender: Addr, funds: Vec<Coin>, data: T) -> Self {
236        ContractMessageContent {
237            sender,
238            funds,
239            data,
240        }
241    }
242
243    pub fn new_with_info(info: MessageInfo, signer: Addr, data: T) -> Self {
244        ContractMessageContent {
245            sender: signer,
246            funds: info.funds,
247            data,
248        }
249    }
250}
251
252impl<T> From<ContractMessageContent<T>> for LegacyContractMessageContent<T> {
253    fn from(value: ContractMessageContent<T>) -> Self {
254        LegacyContractMessageContent {
255            sender: value.sender,
256            proxy: None,
257            funds: value.funds,
258            data: value.data,
259        }
260    }
261}
262
263#[derive(Serialize)]
264pub struct LegacyContractMessageContent<T> {
265    pub sender: Addr,
266    pub proxy: Option<Addr>,
267    pub funds: Vec<Coin>,
268    pub data: T,
269}
270
271impl<T> SigningPurpose for LegacyContractMessageContent<T>
272where
273    T: SigningPurpose,
274{
275    fn message_type() -> MessageType {
276        T::message_type()
277    }
278}