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;
45pub mod versioning;
46
47#[derive(Clone, Debug, clap::Subcommand)]
48#[allow(clippy::large_enum_variant)]
49pub enum SmartContractsCmd {
51 GetScripts(get_scripts::GetScripts),
53 UpsertDParameter(d_parameter::UpsertDParameterCmd),
55 UpsertPermissionedCandidates(permissioned_candidates::UpsertPermissionedCandidatesCmd),
57 Register(register::RegisterCmd),
59 Deregister(register::DeregisterCmd),
61 #[command(subcommand)]
62 Reserve(reserve::ReserveCmd),
64 #[command(subcommand)]
65 Governance(governance::GovernanceCmd),
67 AssembleAndSubmitTx(assemble_tx::AssembleAndSubmitCmd),
69 SignTx(sign_tx::SignTxCmd),
71 #[command(subcommand)]
72 GovernedMap(governed_map::GovernedMapCmd),
74 #[command(subcommand)]
75 Bridge(bridge::BridgeCmd),
77 UpsertScript(versioning::UpsertScriptCmd),
79}
80
81#[derive(Clone, Debug, clap::Parser)]
82#[command(author, version, about, long_about = None)]
83pub struct CommonArguments {
85 #[arg(default_value = "ws://localhost:1337", long, short = 'O', env)]
86 ogmios_url: String,
88 #[arg(default_value = "180", long, env)]
89 ogmios_requests_timeout_seconds: u64,
91 #[arg(default_value = "5", long)]
92 retry_delay_seconds: u64,
95 #[arg(default_value = "59", long)]
96 retry_count: usize,
99}
100
101impl CommonArguments {
102 pub async fn get_ogmios_client(&self) -> crate::CmdResult<OgmiosClients> {
104 Ok(client_for_url(
105 &self.ogmios_url,
106 Duration::from_secs(self.ogmios_requests_timeout_seconds),
107 )
108 .await
109 .map_err(|e| format!("Failed to connect to Ogmios at {} with: {}", &self.ogmios_url, e))?)
110 }
111
112 pub fn retries(&self) -> FixedDelayRetries {
114 FixedDelayRetries::new(Duration::from_secs(self.retry_delay_seconds), self.retry_count)
115 }
116}
117
118type CmdResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
120type SubCmdResult = Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>>;
122
123impl SmartContractsCmd {
124 pub async fn execute(self) -> CmdResult<()> {
126 let result: serde_json::Value = match self {
127 Self::Governance(cmd) => cmd.execute().await,
128 Self::GetScripts(cmd) => cmd.execute().await,
129 Self::UpsertDParameter(cmd) => cmd.execute().await,
130 Self::UpsertPermissionedCandidates(cmd) => cmd.execute().await,
131 Self::Register(cmd) => cmd.execute().await,
132 Self::Deregister(cmd) => cmd.execute().await,
133 Self::Reserve(cmd) => cmd.execute().await,
134 Self::AssembleAndSubmitTx(cmd) => cmd.execute().await,
135 Self::SignTx(cmd) => cmd.execute().await,
136 Self::GovernedMap(cmd) => cmd.execute().await,
137 Self::Bridge(cmd) => cmd.execute().await,
138 Self::UpsertScript(cmd) => cmd.execute().await,
139 }?;
140 println!("{}", result);
141 Ok(())
142 }
143
144 pub fn execute_blocking(self) -> CmdResult<()> {
146 tokio::runtime::Runtime::new()?.block_on(self.execute())
147 }
148}
149
150pub(crate) fn transaction_submitted_json(tx_hash: McTxHash) -> serde_json::Value {
153 serde_json::json!(MultiSigSmartContractResult::TransactionSubmitted(tx_hash))
154}
155
156pub(crate) fn option_to_json<T: Serialize>(value_opt: Option<T>) -> serde_json::Value {
158 match value_opt {
159 Some(value) => serde_json::json!(value),
160 None => serde_json::json!({}),
161 }
162}
163
164#[derive(Clone, Debug, clap::Parser)]
165pub(crate) struct PaymentFilePath {
166 #[arg(long, short = 'k')]
167 payment_key_file: String,
169}
170
171impl PaymentFilePath {
172 pub(crate) fn read_key(&self) -> CmdResult<CardanoPaymentSigningKey> {
174 let key_file = CardanoKeyFileContent::parse_file(&self.payment_key_file)?;
175 Ok(CardanoPaymentSigningKey::try_from(key_file)?)
176 }
177}
178
179#[derive(Clone, Debug, clap::Parser)]
180pub(crate) struct GenesisUtxo {
181 #[arg(long, short = 'c')]
182 genesis_utxo: UtxoId,
184}
185
186impl From<GenesisUtxo> for UtxoId {
187 fn from(value: GenesisUtxo) -> Self {
188 value.genesis_utxo
189 }
190}
191
192#[cfg(test)]
193mod test {
194 use std::str::FromStr;
195
196 use hex_literal::hex;
197 use sidechain_domain::{
198 AuraPublicKey, CandidateKey, CandidateKeys, GrandpaPublicKey, PermissionedCandidateData,
199 SidechainPublicKey,
200 };
201
202 #[test]
203 fn parse_partnerchain_public_keys_legacy_format_without_0x_prefix() {
204 let input = "039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180:e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73:cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
205 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
206 }
207
208 #[test]
209 fn parse_partnerchain_public_keys_legacy_format_with_0x_prefix() {
210 let input = "0x039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180:0xe85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73:0xcdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
211 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
212 }
213
214 #[test]
215 fn parse_partnerchain_public_keys_generic_format_without_0x_prefix() {
216 let input = "039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180,aura:e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73,gran:cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
217 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
218 }
219
220 #[test]
221 fn parse_partnerchain_public_keys_generic_format_with_0x_prefix() {
222 let input = "0x039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180,aura:0xe85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73,gran:0xcdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55";
223 assert_eq!(PermissionedCandidateData::from_str(input).unwrap(), expected_public_keys())
224 }
225
226 #[test]
227 fn key_id_can_contain_0x() {
228 let input = "0x0102,0xxd:0xffff";
229 assert_eq!(
230 PermissionedCandidateData::from_str(input).unwrap(),
231 PermissionedCandidateData {
232 sidechain_public_key: SidechainPublicKey([1, 2].to_vec()),
233 keys: CandidateKeys(vec![CandidateKey {
234 id: *b"0xxd",
235 bytes: [255, 255].to_vec()
236 }])
237 }
238 )
239 }
240
241 fn expected_public_keys() -> PermissionedCandidateData {
242 PermissionedCandidateData {
243 sidechain_public_key: SidechainPublicKey(
244 hex!("039799ff93d184146deacaa455dade51b13ed16f23cdad11d1ad6af20103391180").to_vec(),
245 ),
246 keys: CandidateKeys(vec![
247 AuraPublicKey(
248 hex!("e85534c93315d60f808568d1dce5cb9e8ba6ed0b204209c5cc8f3bec56c10b73")
249 .to_vec(),
250 )
251 .into(),
252 GrandpaPublicKey(
253 hex!("cdf3e5b33f53c8b541bbaea383225c45654f24de38c585725f3cff25b2802f55")
254 .to_vec(),
255 )
256 .into(),
257 ]),
258 }
259 }
260}