Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETCM-8753: implement deregister command #318

Merged
merged 4 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 3 additions & 12 deletions toolkit/cli/smart-contracts-commands/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub enum SmartContractsCmd {
UpsertDParameter(d_parameter::UpsertDParameterCmd),
/// Register candidate
Register(register::RegisterCmd),
/// Deregister candidate
Deregister(register::DeregisterCmd),
}

#[derive(Clone, Debug, clap::Parser)]
Expand All @@ -34,6 +36,7 @@ impl SmartContractsCmd {
Self::GetScripts(cmd) => cmd.execute().await,
Self::UpsertDParameter(cmd) => cmd.execute().await,
Self::Register(cmd) => cmd.execute().await,
Self::Deregister(cmd) => cmd.execute().await,
}
}

Expand Down Expand Up @@ -76,18 +79,6 @@ pub(crate) fn parse_partnerchain_public_keys(
}
}

fn payment_signing_key_to_mainchain_address_hash(
payment_signing_key: MainchainPrivateKey,
) -> CmdResult<MainchainAddressHash> {
Ok(cardano_serialization_lib::PrivateKey::from_normal_bytes(&payment_signing_key.0)?
.to_public()
.hash()
.to_bytes()
.as_slice()
.try_into()
.map(MainchainAddressHash)?)
}

#[cfg(test)]
mod test {
use crate::parse_partnerchain_public_keys;
Expand Down
39 changes: 36 additions & 3 deletions toolkit/cli/smart-contracts-commands/src/register.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use jsonrpsee::http_client::HttpClient;
use partner_chains_cardano_offchain::{await_tx::FixedDelayRetries, register::run_register};
use partner_chains_cardano_offchain::{
await_tx::FixedDelayRetries,
register::{run_deregister, run_register},
};
use sidechain_domain::{
AdaBasedStaking, CandidateRegistration, MainchainPublicKey, MainchainSignature,
PermissionedCandidateData, SidechainSignature, UtxoId,
Expand All @@ -21,7 +24,7 @@ pub struct RegisterCmd {
long,
value_name = "PARTNERCHAIN_KEY:AURA_KEY:GRANDPA_KEY",
alias = "sidechain-public-keys",
value_parser=parse_partnerchain_public_keys
value_parser = parse_partnerchain_public_keys
)]
partnerchain_public_keys: PermissionedCandidateData,
#[arg(long, alias = "sidechain-signature")]
Expand All @@ -43,7 +46,7 @@ impl RegisterCmd {
},
partnerchain_pub_key: self.partnerchain_public_keys.sidechain_public_key,
partnerchain_signature: self.partnerchain_signature,
own_pkh: crate::payment_signing_key_to_mainchain_address_hash(payment_key.clone())?,
own_pkh: payment_key.to_pub_key_hash(),
registration_utxo: self.registration_utxo,
aura_pub_key: self.partnerchain_public_keys.aura_public_key,
grandpa_pub_key: self.partnerchain_public_keys.grandpa_public_key,
Expand All @@ -61,3 +64,33 @@ impl RegisterCmd {
Ok(())
}
}

#[derive(Clone, Debug, clap::Parser)]
pub struct DeregisterCmd {
#[clap(flatten)]
common_arguments: crate::CommonArguments,
#[arg(long)]
genesis_utxo: UtxoId,
#[arg(long)]
payment_key_file: String,
#[arg(long)]
spo_public_key: MainchainPublicKey,
}

impl DeregisterCmd {
pub async fn execute(self) -> crate::CmdResult<()> {
let payment_signing_key = read_private_key_from_file(&self.payment_key_file)?;
let client = HttpClient::builder().build(self.common_arguments.ogmios_url)?;

run_deregister(
self.genesis_utxo,
payment_signing_key,
self.spo_public_key,
&client,
FixedDelayRetries::two_minutes(),
)
.await?;

Ok(())
}
}
6 changes: 2 additions & 4 deletions toolkit/offchain/src/init_governance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use ogmios_client::{
types::{OgmiosTx, OgmiosUtxo},
};
use partner_chains_plutus_data::version_oracle::VersionOracleDatum;
use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey, McTxHash, UtxoId, UtxoIndex};
use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey, UtxoId};

#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -128,9 +128,7 @@ pub async fn run_init_governance<
let result = client.submit_transaction(&signed_transaction.to_bytes()).await?;
let tx_id = result.transaction.id;
log::info!("✅ Transaction submitted. ID: {}", hex::encode(result.transaction.id));
await_tx
.await_tx_output(client, UtxoId { tx_hash: McTxHash(tx_id), index: UtxoIndex(0) })
.await?;
await_tx.await_tx_output(client, UtxoId::new(tx_id, 0)).await?;

Ok((genesis_utxo.to_domain(), result.transaction))
}
Expand Down
120 changes: 115 additions & 5 deletions toolkit/offchain/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use ogmios_client::{
query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
query_network::QueryNetwork,
transactions::Transactions,
types::{OgmiosTx, OgmiosUtxo},
types::OgmiosUtxo,
};
use partner_chains_plutus_data::registered_candidates::{
candidate_registration_to_plutus_data, RegisterValidatorDatum,
Expand All @@ -32,7 +32,7 @@ pub trait Register {
genesis_utxo: UtxoId,
candidate_registration: &CandidateRegistration,
payment_signing_key: MainchainPrivateKey,
) -> Result<Option<OgmiosTx>, OffchainError>;
) -> Result<Option<McTxHash>, OffchainError>;
}

impl<T> Register for T
Expand All @@ -44,7 +44,7 @@ where
genesis_utxo: UtxoId,
candidate_registration: &CandidateRegistration,
payment_signing_key: MainchainPrivateKey,
) -> Result<Option<OgmiosTx>, OffchainError> {
) -> Result<Option<McTxHash>, OffchainError> {
run_register(
genesis_utxo,
candidate_registration,
Expand All @@ -66,7 +66,7 @@ pub async fn run_register<
payment_signing_key: MainchainPrivateKey,
ogmios_client: &C,
await_tx: A,
) -> anyhow::Result<Option<OgmiosTx>> {
) -> anyhow::Result<Option<McTxHash>> {
let ctx = TransactionContext::for_payment_key(payment_signing_key.0, ogmios_client).await?;
let validator = crate::scripts_data::registered_candidates_scripts(genesis_utxo)?;
let validator_address = validator.address_bech32(ctx.network)?;
Expand Down Expand Up @@ -132,7 +132,93 @@ pub async fn run_register<
log::info!("✅ Transaction submitted. ID: {}", hex::encode(result.transaction.id));
await_tx.await_tx_output(ogmios_client, UtxoId::new(tx_id, 0)).await?;

Ok(Some(result.transaction))
Ok(Some(McTxHash(result.transaction.id)))
}

pub trait Deregister {
#[allow(async_fn_in_trait)]
async fn deregister(
&self,
genesis_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
stake_ownership_pub_key: MainchainPublicKey,
) -> Result<Option<McTxHash>, OffchainError>;
}

impl<T> Deregister for T
where
T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
{
async fn deregister(
&self,
genesis_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
stake_ownership_pub_key: MainchainPublicKey,
) -> Result<Option<McTxHash>, OffchainError> {
run_deregister(
genesis_utxo,
payment_signing_key,
stake_ownership_pub_key,
self,
FixedDelayRetries::two_minutes(),
)
.await
.map_err(|e| OffchainError::InternalError(e.to_string()))
}
}

pub async fn run_deregister<
C: QueryLedgerState + QueryNetwork + QueryUtxoByUtxoId + Transactions,
A: AwaitTx,
>(
genesis_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
stake_ownership_pub_key: MainchainPublicKey,
ogmios_client: &C,
await_tx: A,
) -> anyhow::Result<Option<McTxHash>> {
let ctx = TransactionContext::for_payment_key(payment_signing_key.0, ogmios_client).await?;
let validator = crate::scripts_data::registered_candidates_scripts(genesis_utxo)?;
let validator_address = validator.address_bech32(ctx.network)?;
let all_registration_utxos = ogmios_client.query_utxos(&[validator_address]).await?;
let own_registrations = get_own_registrations(
payment_signing_key.to_pub_key_hash(),
stake_ownership_pub_key.clone(),
&all_registration_utxos,
);

if own_registrations.is_empty() {
log::info!("✅ Candidate is not registered.");
return Ok(None);
}

let own_registration_utxos = own_registrations.iter().map(|r| r.0.clone()).collect::<Vec<_>>();
kpinter-iohk marked this conversation as resolved.
Show resolved Hide resolved
let zero_ex_units = ExUnits::new(&0u64.into(), &0u64.into());
let tx = deregister_tx(&validator, &own_registration_utxos, &ctx, zero_ex_units)?;

let evaluate_response =
ogmios_client.evaluate_transaction(&tx.to_bytes()).await.map_err(|e| {
anyhow!(
"Evaluate candidate deregistration transaction request failed: {}, bytes: {}",
e,
hex::encode(tx.to_bytes())
)
})?;
let validator_redeemer_ex_units = get_first_validator_budget(evaluate_response)?;
let tx = deregister_tx(&validator, &own_registration_utxos, &ctx, validator_redeemer_ex_units)?;
let signed_tx = ctx.sign(&tx).to_bytes();
let result = ogmios_client.submit_transaction(&signed_tx).await.map_err(|e| {
anyhow!(
"Submit candidate deregistration transaction request failed: {}, bytes: {}",
e,
hex::encode(tx.to_bytes())
)
})?;
let tx_id = result.transaction.id;
log::info!("✅ Transaction submitted. ID: {}", hex::encode(result.transaction.id));
await_tx.await_tx_output(ogmios_client, UtxoId::new(tx_id, 0)).await?;

Ok(Some(McTxHash(result.transaction.id)))
}

fn get_own_registrations(
Expand Down Expand Up @@ -210,6 +296,30 @@ fn register_tx(
tx_builder.balance_update_and_build(ctx)
}

fn deregister_tx(
validator: &PlutusScript,
own_registration_utxos: &[OgmiosUtxo],
ctx: &TransactionContext,
validator_redeemer_ex_units: ExUnits,
) -> Result<Transaction, JsError> {
let config = crate::csl::get_builder_config(ctx)?;
let mut tx_builder = TransactionBuilder::new(&config);

{
let mut inputs = TxInputsBuilder::new();
for own_registration_utxo in own_registration_utxos {
kpinter-iohk marked this conversation as resolved.
Show resolved Hide resolved
inputs.add_script_utxo_input(
own_registration_utxo,
validator,
validator_redeemer_ex_units.clone(),
kpinter-iohk marked this conversation as resolved.
Show resolved Hide resolved
)?;
}
tx_builder.set_inputs(&inputs);
}

tx_builder.balance_update_and_build(ctx)
}
LGLO marked this conversation as resolved.
Show resolved Hide resolved

#[cfg(test)]
mod tests {
use super::register_tx;
Expand Down
2 changes: 1 addition & 1 deletion toolkit/offchain/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ async fn run_register<T: QueryLedgerState + Transactions + QueryNetwork + QueryU
genesis_utxo: UtxoId,
partnerchain_signature: SidechainSignature,
client: &T,
) -> Option<OgmiosTx> {
) -> Option<McTxHash> {
let eve_utxos = client.query_utxos(&[EVE_ADDRESS.to_string()]).await.unwrap();
let registration_utxo = eve_utxos.first().unwrap().utxo_id();
client
Expand Down
14 changes: 1 addition & 13 deletions toolkit/partner-chains-cli/src/cardano_key.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::IOContext;
use anyhow::anyhow;
use sidechain_domain::{MainchainAddressHash, MainchainPrivateKey};
use sidechain_domain::MainchainPrivateKey;

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -45,15 +45,3 @@ pub(crate) fn get_mc_pkey_from_file(
) -> anyhow::Result<MainchainPrivateKey> {
Ok(MainchainPrivateKey(get_key_bytes_from_file(path, context)?))
}

pub(crate) fn get_mc_address_hash_from_pkey(pkey: &MainchainPrivateKey) -> MainchainAddressHash {
let csl_private_key = cardano_serialization_lib::PrivateKey::from_normal_bytes(&pkey.0)
.expect("Conversion is infallible");
let csl_public_key_hash = csl_private_key
.to_public()
.hash()
.to_bytes()
.try_into()
.expect("Bytes represent correct public key hash");
MainchainAddressHash(csl_public_key_hash)
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn get_private_key_and_key_hash<C: IOContext>(
let cardano_signig_key_file = config_fields::CARDANO_PAYMENT_SIGNING_KEY_FILE
.prompt_with_default_from_file_and_save(context);
let pkey = cardano_key::get_mc_pkey_from_file(&cardano_signig_key_file, context)?;
let addr_hash = cardano_key::get_mc_address_hash_from_pkey(&pkey);
let addr_hash = pkey.to_pub_key_hash();

Ok((pkey, addr_hash))
}
Expand Down
2 changes: 2 additions & 0 deletions toolkit/primitives/domain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ lazy_static = { workspace = true }
blake2b_simd = { workspace = true }
figment = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
cardano-serialization-lib = { workspace = true, optional = true }

[dev-dependencies]
serde_json = { workspace = true }
Expand All @@ -38,6 +39,7 @@ std = [
"num-bigint/std",
"figment",
"thiserror",
"cardano-serialization-lib",
]
serde = [
"dep:serde",
Expand Down
15 changes: 15 additions & 0 deletions toolkit/primitives/domain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@ impl core::fmt::Debug for MainchainPrivateKey {
}
}

impl MainchainPrivateKey {
#[cfg(feature = "std")]
pub fn to_pub_key_hash(&self) -> MainchainAddressHash {
cardano_serialization_lib::PrivateKey::from_normal_bytes(&self.0)
.expect("Conversion cannot fail on valid MainchainPrivateKey values")
.to_public()
.hash()
.to_bytes()
.as_slice()
.try_into()
.map(MainchainAddressHash)
.expect("Conversion cannot fail as representation is the same")
}
}

impl TryFrom<Vec<u8>> for MainchainPublicKey {
type Error = &'static str;

Expand Down
Loading