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