partner_chains_smart_contracts_commands/
lib.rs1use ogmios_client::jsonrpsee::{OgmiosClients, client_for_url};
26use partner_chains_cardano_offchain::{
27 await_tx::FixedDelayRetries,
28 cardano_keys::{CardanoKeyFileContent, CardanoPaymentSigningKey},
29 multisig::MultiSigSmartContractResult,
30};
31use serde::Serialize;
32use sidechain_domain::*;
33use std::time::Duration;
34
35pub mod assemble_tx;
36pub mod bridge;
37pub mod d_parameter;
38pub mod get_scripts;
39pub mod governance;
40pub mod governed_map;
41pub mod permissioned_candidates;
42pub mod register;
43pub mod reserve;
44pub mod sign_tx;
45
46#[derive(Clone, Debug, clap::Subcommand)]
47#[allow(clippy::large_enum_variant)]
48pub enum SmartContractsCmd {
50 GetScripts(get_scripts::GetScripts),
52 UpsertDParameter(d_parameter::UpsertDParameterCmd),
54 UpsertPermissionedCandidates(permissioned_candidates::UpsertPermissionedCandidatesCmd),
56 Register(register::RegisterCmd),
58 Deregister(register::DeregisterCmd),
60 #[command(subcommand)]
61 Reserve(reserve::ReserveCmd),
63 #[command(subcommand)]
64 Governance(governance::GovernanceCmd),
66 AssembleAndSubmitTx(assemble_tx::AssembleAndSubmitCmd),
68 SignTx(sign_tx::SignTxCmd),
70 #[command(subcommand)]
71 GovernedMap(governed_map::GovernedMapCmd),
73 #[command(subcommand)]
74 Bridge(bridge::BridgeCmd),
76}
77
78#[derive(Clone, Debug, clap::Parser)]
79#[command(author, version, about, long_about = None)]
80pub struct CommonArguments {
82 #[arg(default_value = "ws://localhost:1337", long, short = 'O', env)]
83 ogmios_url: String,
85 #[arg(default_value = "180", long, env)]
86 ogmios_requests_timeout_seconds: u64,
88 #[arg(default_value = "5", long)]
89 retry_delay_seconds: u64,
92 #[arg(default_value = "59", long)]
93 retry_count: usize,
96}
97
98impl CommonArguments {
99 pub async fn get_ogmios_client(&self) -> crate::CmdResult<OgmiosClients> {
101 Ok(client_for_url(
102 &self.ogmios_url,
103 Duration::from_secs(self.ogmios_requests_timeout_seconds),
104 )
105 .await
106 .map_err(|e| format!("Failed to connect to Ogmios at {} with: {}", &self.ogmios_url, e))?)
107 }
108
109 pub fn retries(&self) -> FixedDelayRetries {
111 FixedDelayRetries::new(Duration::from_secs(self.retry_delay_seconds), self.retry_count)
112 }
113}
114
115type CmdResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
117type SubCmdResult = Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>>;
119
120impl SmartContractsCmd {
121 pub async fn execute(self) -> CmdResult<()> {
123 let result: serde_json::Value = match self {
124 Self::Governance(cmd) => cmd.execute().await,
125 Self::GetScripts(cmd) => cmd.execute().await,
126 Self::UpsertDParameter(cmd) => cmd.execute().await,
127 Self::UpsertPermissionedCandidates(cmd) => cmd.execute().await,
128 Self::Register(cmd) => cmd.execute().await,
129 Self::Deregister(cmd) => cmd.execute().await,
130 Self::Reserve(cmd) => cmd.execute().await,
131 Self::AssembleAndSubmitTx(cmd) => cmd.execute().await,
132 Self::SignTx(cmd) => cmd.execute().await,
133 Self::GovernedMap(cmd) => cmd.execute().await,
134 Self::Bridge(cmd) => cmd.execute().await,
135 }?;
136 println!("{}", result);
137 Ok(())
138 }
139
140 pub fn execute_blocking(self) -> CmdResult<()> {
142 tokio::runtime::Runtime::new()?.block_on(self.execute())
143 }
144}
145
146pub(crate) fn transaction_submitted_json(tx_hash: McTxHash) -> serde_json::Value {
149 serde_json::json!(MultiSigSmartContractResult::TransactionSubmitted(tx_hash))
150}
151
152pub(crate) fn option_to_json<T: Serialize>(value_opt: Option<T>) -> serde_json::Value {
154 match value_opt {
155 Some(value) => serde_json::json!(value),
156 None => serde_json::json!({}),
157 }
158}
159
160#[derive(Clone, Debug, clap::Parser)]
161pub(crate) struct PaymentFilePath {
162 #[arg(long, short = 'k')]
163 payment_key_file: String,
165}
166
167impl PaymentFilePath {
168 pub(crate) fn read_key(&self) -> CmdResult<CardanoPaymentSigningKey> {
170 let key_file = CardanoKeyFileContent::parse_file(&self.payment_key_file)?;
171 Ok(CardanoPaymentSigningKey::try_from(key_file)?)
172 }
173}
174
175#[derive(Clone, Debug, clap::Parser)]
176pub(crate) struct GenesisUtxo {
177 #[arg(long, short = 'c')]
178 genesis_utxo: UtxoId,
180}
181
182impl From<GenesisUtxo> for UtxoId {
183 fn from(value: GenesisUtxo) -> Self {
184 value.genesis_utxo
185 }
186}
187
188#[cfg(test)]
189mod test {
190 use std::str::FromStr;
191
192 use hex_literal::hex;
193 use sidechain_domain::{
194 AuraPublicKey, CandidateKey, CandidateKeys, GrandpaPublicKey, PermissionedCandidateData,
195 SidechainPublicKey,
196 };
197
198 #[test]
199 fn parse_partnerchain_public_keys_legacy_format_without_0x_prefix() {
200 let input = "039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180:e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73:cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
201 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
202 }
203
204 #[test]
205 fn parse_partnerchain_public_keys_legacy_format_with_0x_prefix() {
206 let input = "0x039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180:0xe85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73:0xcdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
207 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
208 }
209
210 #[test]
211 fn parse_partnerchain_public_keys_generic_format_without_0x_prefix() {
212 let input = "039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180,aura:e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73,gran:cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
213 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
214 }
215
216 #[test]
217 fn parse_partnerchain_public_keys_generic_format_with_0x_prefix() {
218 let input = "0x039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180,aura:0xe85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73,gran:0xcdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
219 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
220 }
221
222 #[test]
223 fn key_id_can_contain_0x() {
224 let input = "0x0102,0xxd:0xffff";
225 assert_eq!(
226 PermissionedCandidateData::from_str(input).unwrap(),
227 PermissionedCandidateData {
228 sidechain_public_key: SidechainPublicKey([1, 2].to_vec()),
229 keys: CandidateKeys(vec![CandidateKey {
230 id: *b"0xxd",
231 bytes: [255, 255].to_vec()
232 }])
233 }
234 )
235 }
236
237 fn expected_public_keys() -> PermissionedCandidateData {
238 PermissionedCandidateData {
239 sidechain_public_key: SidechainPublicKey(
240 hex!("039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180").to_vec(),
241 ),
242 keys: CandidateKeys(vec![
243 AuraPublicKey(
244 hex!("e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73")
245 .to_vec(),
246 )
247 .into(),
248 GrandpaPublicKey(
249 hex!("cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55")
250 .to_vec(),
251 )
252 .into(),
253 ]),
254 }
255 }
256}