1use crate::cmd_traits::{GetPermissionedCandidates, UpsertPermissionedCandidates};
2use authority_selection_inherents::MaybeFromCandidateKeys;
3use ogmios_client::query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId};
4use ogmios_client::query_network::QueryNetwork;
5use ogmios_client::transactions::Transactions;
6use partner_chains_cardano_offchain::await_tx::FixedDelayRetries;
7use partner_chains_cardano_offchain::cardano_keys::CardanoPaymentSigningKey;
8use partner_chains_cardano_offchain::csl::NetworkTypeExt;
9use partner_chains_cardano_offchain::multisig::MultiSigSmartContractResult;
10use partner_chains_cardano_offchain::permissioned_candidates::{
11 get_permissioned_candidates, upsert_permissioned_candidates,
12};
13use serde::{Deserialize, Deserializer, Serialize};
14use sidechain_domain::byte_string::ByteString;
15use sidechain_domain::{CandidateKey, CandidateKeys, PermissionedCandidateData, UtxoId};
16use sp_core::crypto::AccountId32;
17use sp_core::ecdsa;
18use sp_runtime::traits::{IdentifyAccount, OpaqueKeys};
19use std::collections::BTreeMap;
20use std::fmt::{Display, Formatter};
21
22#[derive(Debug, Serialize, Eq, PartialEq)]
24pub struct PermissionedCandidateKeys {
25 pub partner_chains_key: ByteString,
27 pub keys: BTreeMap<String, ByteString>,
29}
30
31impl PermissionedCandidateKeys {
32 fn keys_sorted(&self) -> Vec<(String, ByteString)> {
33 let mut v: Vec<_> = self.keys.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
34 v.sort();
35 v
36 }
37}
38
39impl Display for PermissionedCandidateKeys {
40 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41 write!(f, "Partner Chains Key: {}", self.partner_chains_key.to_hex_string())?;
42 for (id, bytes) in self.keys_sorted().iter() {
43 write!(f, ", {id}: {}", bytes.to_hex_string())?
44 }
45 Ok(())
46 }
47}
48
49impl From<&sidechain_domain::PermissionedCandidateData> for PermissionedCandidateKeys {
50 fn from(value: &sidechain_domain::PermissionedCandidateData) -> Self {
51 Self {
52 partner_chains_key: ByteString::from(value.sidechain_public_key.0.clone()),
53 keys: value
54 .keys
55 .0
56 .iter()
57 .map(|ck| {
58 (
59 String::from_utf8(ck.id.to_vec()).expect("key type ids are valid utf-8"),
60 ByteString::from(ck.bytes.clone()),
61 )
62 })
63 .collect(),
64 }
65 }
66}
67
68impl TryFrom<&PermissionedCandidateKeys> for sidechain_domain::CandidateKeys {
69 type Error = String;
70
71 fn try_from(value: &PermissionedCandidateKeys) -> Result<Self, Self::Error> {
72 let mut acc = vec![];
73 for (k, v) in value.keys.iter() {
74 let id: [u8; 4] = k
75 .as_bytes()
76 .try_into()
77 .map_err(|_| format!("Could not parse key type id: '{k}'"))?;
78 let bytes = v.0.clone();
79 acc.push(CandidateKey { id, bytes });
80 }
81 Ok(CandidateKeys(acc))
82 }
83}
84
85#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd)]
87pub struct ParsedPermissionedCandidatesKeys<Keys> {
88 pub sidechain: ecdsa::Public,
90 pub keys: Keys,
92}
93
94impl<T> ParsedPermissionedCandidatesKeys<T> {
95 pub fn account_id_32(&self) -> AccountId32 {
97 sp_runtime::MultiSigner::from(self.sidechain).into_account()
98 }
99}
100
101impl<Keys: MaybeFromCandidateKeys> TryFrom<&PermissionedCandidateKeys>
102 for ParsedPermissionedCandidatesKeys<Keys>
103{
104 type Error = anyhow::Error;
105
106 fn try_from(value: &PermissionedCandidateKeys) -> Result<Self, Self::Error> {
107 let sidechain = parse_ecdsa(&value.partner_chains_key.0).ok_or(anyhow::Error::msg(
108 format!("'{}' is invalid ECDSA public key", value.partner_chains_key.to_hex_string()),
109 ))?;
110 let candidate_keys = CandidateKeys::try_from(value).map_err(|e| anyhow::anyhow!(e))?;
111 let keys = MaybeFromCandidateKeys::maybe_from(&candidate_keys)
112 .ok_or(anyhow::anyhow!("Could not parse candidate keys!"))?;
113 Ok(Self { sidechain, keys })
114 }
115}
116
117impl<Keys: OpaqueKeys> From<&ParsedPermissionedCandidatesKeys<Keys>>
118 for sidechain_domain::PermissionedCandidateData
119{
120 fn from(value: &ParsedPermissionedCandidatesKeys<Keys>) -> Self {
121 let keys = Keys::key_ids()
122 .iter()
123 .map(|key_id| {
124 let bytes = value.keys.get_raw(*key_id).to_vec();
125 CandidateKey { id: key_id.0, bytes }
126 })
127 .collect();
128 Self {
129 sidechain_public_key: sidechain_domain::SidechainPublicKey(value.sidechain.0.to_vec()),
130 keys: CandidateKeys(keys),
131 }
132 }
133}
134
135fn parse_ecdsa(bytes: &[u8]) -> Option<ecdsa::Public> {
136 Some(ecdsa::Public::from(<[u8; 33]>::try_from(bytes).ok()?))
137}
138
139impl<C: QueryLedgerState + QueryNetwork + Transactions + QueryUtxoByUtxoId>
140 UpsertPermissionedCandidates for C
141{
142 async fn upsert_permissioned_candidates(
143 &self,
144 await_tx: FixedDelayRetries,
145 genesis_utxo: UtxoId,
146 candidates: &[PermissionedCandidateData],
147 payment_signing_key: &CardanoPaymentSigningKey,
148 ) -> anyhow::Result<Option<MultiSigSmartContractResult>> {
149 upsert_permissioned_candidates(
150 genesis_utxo,
151 candidates,
152 payment_signing_key,
153 self,
154 &await_tx,
155 )
156 .await
157 }
158}
159
160impl<C: QueryLedgerState + QueryNetwork> GetPermissionedCandidates for C {
161 async fn get_permissioned_candidates(
162 &self,
163 genesis_utxo: UtxoId,
164 ) -> anyhow::Result<Option<Vec<PermissionedCandidateData>>> {
165 let network = self.shelley_genesis_configuration().await?.network.to_csl();
166 get_permissioned_candidates(genesis_utxo, network, self).await
167 }
168}
169
170impl<'de> Deserialize<'de> for PermissionedCandidateKeys {
172 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173 where
174 D: Deserializer<'de>,
175 {
176 CandidateKeysFormat::deserialize(deserializer).map(From::from)
177 }
178}
179
180#[derive(Deserialize)]
181#[serde(untagged)]
182enum CandidateKeysFormat {
183 V1 { partner_chains_key: ByteString, keys: BTreeMap<String, ByteString> },
184 V0 { sidechain_pub_key: ByteString, aura_pub_key: ByteString, grandpa_pub_key: ByteString },
185}
186
187impl From<CandidateKeysFormat> for PermissionedCandidateKeys {
188 fn from(v: CandidateKeysFormat) -> Self {
189 match v {
190 CandidateKeysFormat::V1 { partner_chains_key, keys } => {
191 Self { partner_chains_key, keys }
192 },
193 CandidateKeysFormat::V0 { sidechain_pub_key, aura_pub_key, grandpa_pub_key } => Self {
194 partner_chains_key: sidechain_pub_key,
195 keys: vec![("aura".to_owned(), aura_pub_key), ("gran".to_owned(), grandpa_pub_key)]
196 .into_iter()
197 .collect(),
198 },
199 }
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use sidechain_domain::byte_string::ByteString;
206
207 use super::PermissionedCandidateKeys;
208
209 #[test]
210 fn candidate_keys_serde_round_trip() {
211 let keys = PermissionedCandidateKeys {
212 partner_chains_key: ByteString::from_hex_unsafe("0x010101"),
213 keys: vec![
214 ("key1".to_owned(), ByteString::from_hex_unsafe("0x020202")),
215 ("key2".to_owned(), ByteString::from_hex_unsafe("0x030303")),
216 ]
217 .into_iter()
218 .collect(),
219 };
220 let json = serde_json::to_value(&keys).unwrap();
221 let deserialized = serde_json::from_value(json).unwrap();
222 assert_eq!(keys, deserialized)
223 }
224
225 #[test]
226 fn v0_deserialization() {
227 let deserialized: PermissionedCandidateKeys = serde_json::from_value(serde_json::json!({
228 "sidechain_pub_key": "0x0101",
229 "aura_pub_key": "0x0202",
230 "grandpa_pub_key": "0x0303"
231 }))
232 .unwrap();
233 assert_eq!(
234 deserialized,
235 PermissionedCandidateKeys {
236 partner_chains_key: ByteString::from_hex_unsafe("0x0101"),
237 keys: vec![
238 ("aura".to_owned(), ByteString::from_hex_unsafe("0x0202")),
239 ("gran".to_owned(), ByteString::from_hex_unsafe("0x0303")),
240 ]
241 .into_iter()
242 .collect(),
243 }
244 )
245 }
246}