Skip to content

Commit

Permalink
implement register cli
Browse files Browse the repository at this point in the history
  • Loading branch information
kpinter-iohk committed Dec 5, 2024
1 parent 77c8f07 commit 09ed96c
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 48 deletions.
23 changes: 22 additions & 1 deletion toolkit/cli/smart-contracts-commands/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use log4rs::{
append::{console::ConsoleAppender, file::FileAppender},
config::Appender,
};
use sidechain_domain::MainchainPrivateKey;
use sidechain_domain::{AuraPublicKey, GrandpaPublicKey, MainchainPrivateKey, SidechainPublicKey};

pub mod get_scripts;
pub mod init_governance;
pub mod register;

#[derive(Clone, Debug, clap::Subcommand)]
#[allow(clippy::large_enum_variant)]
Expand All @@ -14,6 +15,8 @@ pub enum SmartContractsCmd {
GetScripts(get_scripts::GetScripts),
/// Initialize Partner Chain governance
InitGovernance(init_governance::InitGovernanceCmd),
/// Register candidate
Register(register::RegisterCmd),
}

#[derive(Clone, Debug, clap::Parser)]
Expand All @@ -30,6 +33,7 @@ impl SmartContractsCmd {
match self {
Self::InitGovernance(cmd) => cmd.execute().await,
Self::GetScripts(cmd) => cmd.execute().await,
Self::Register(cmd) => cmd.execute().await,
}
}

Expand Down Expand Up @@ -75,3 +79,20 @@ pub fn setup_logging() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

Ok(())
}

// Parses public keys in formatted as SIDECHAIN_KEY:AURA_KEY:GRANDPA_KEY
pub(crate) fn parse_sidechain_public_keys(
sidechain_public_keys: &str,
) -> CmdResult<(SidechainPublicKey, AuraPublicKey, GrandpaPublicKey)> {
if let [sidechain_pub_key, aura_pub_key, grandpa_pub_key] =
sidechain_public_keys.split(":").collect::<Vec<_>>()[..]
{
Ok((
SidechainPublicKey(hex::decode(sidechain_pub_key)?),
AuraPublicKey(hex::decode(aura_pub_key)?),
GrandpaPublicKey(hex::decode(grandpa_pub_key)?),
))
} else {
Err("Failed to parse sidechain public keys.".into())
}
}
59 changes: 59 additions & 0 deletions toolkit/cli/smart-contracts-commands/src/register.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use jsonrpsee::http_client::HttpClient;
use partner_chains_cardano_offchain::register::run_register;
use sidechain_domain::{
AdaBasedStaking, AuraPublicKey, BlockProducerRegistration, GrandpaPublicKey,
MainchainPublicKey, MainchainSignature, SidechainPublicKey, SidechainSignature, UtxoId,
};

use crate::{parse_sidechain_public_keys, read_private_key_from_file};

#[derive(Clone, Debug, clap::Parser)]
pub struct RegisterCmd {
#[clap(flatten)]
common_arguments: crate::CommonArguments,
#[arg(long)]
genesis_utxo: UtxoId,
#[arg(long)]
registration_utxo: UtxoId,
#[arg(long)]
payment_key_file: String,
#[arg(long)]
stake_signing_key_file: String,
#[arg(long, value_name = "SIDECHAIN_KEY:AURA_KEY:GRANDPA_KEY", value_parser = parse_sidechain_public_keys)]
sidechain_public_keys: (SidechainPublicKey, AuraPublicKey, GrandpaPublicKey),
#[arg(long)]
sidechain_signature: SidechainSignature,
#[arg(long)]
spo_public_key: MainchainPublicKey,
#[arg(long)]
spo_signature: MainchainSignature,
}

impl RegisterCmd {
pub async fn execute(self) -> crate::CmdResult<()> {
let payment_key = read_private_key_from_file(&self.payment_key_file)?;
let client = HttpClient::builder().build(self.common_arguments.ogmios_host)?;
let block_producer_registration = BlockProducerRegistration {
stake_ownership: AdaBasedStaking {
pub_key: self.spo_public_key,
signature: self.spo_signature,
},
sidechain_pub_key: self.sidechain_public_keys.0,
sidechain_signature: self.sidechain_signature,
registration_utxo: self.registration_utxo,
aura_pub_key: self.sidechain_public_keys.1,
grandpa_pub_key: self.sidechain_public_keys.2,
};

run_register(
self.genesis_utxo,
&block_producer_registration,
self.registration_utxo,
payment_key,
&client,
)
.await?;

Ok(())
}
}
2 changes: 1 addition & 1 deletion toolkit/offchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub mod permissioned_candidates;
/// Utilities for handling Plutus script data
mod plutus_script;
/// Supports candidate registration
mod register;
pub mod register;
/// Provides synthetized scripts data
pub mod scripts_data;
#[cfg(test)]
Expand Down
120 changes: 85 additions & 35 deletions toolkit/offchain/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use crate::csl::{
TransactionContext,
};
use crate::plutus_script::PlutusScript;
use crate::OffchainError;
use anyhow::anyhow;
use cardano_serialization_lib::{
BigNum, DataCost, Ed25519KeyHash, ExUnits, JsError, MinOutputAdaCalculator, PlutusData, Transaction, TransactionBuilder, TransactionOutputBuilder, TxInputsBuilder
BigNum, DataCost, Ed25519KeyHash, ExUnits, JsError, MinOutputAdaCalculator, PlutusData,
Transaction, TransactionBuilder, TransactionOutputBuilder, TxInputsBuilder,
};
use ogmios_client::{
query_ledger_state::QueryLedgerState, query_network::QueryNetwork, transactions::Transactions,
Expand All @@ -17,27 +19,63 @@ use partner_chains_plutus_data::registered_candidates::{
block_producer_registration_to_plutus_data, RegisterValidatorDatum,
};
use sidechain_domain::{
BlockProducerRegistration, MainchainAddressHash, MainchainPublicKey, McTxHash, UtxoId,
BlockProducerRegistration, MainchainAddressHash, MainchainPrivateKey, MainchainPublicKey,
McTxHash, UtxoId,
};

pub async fn register<C: QueryLedgerState + QueryNetwork + Transactions>(
pub trait Register {
#[allow(async_fn_in_trait)]
async fn register(
&self,
genesis_utxo: UtxoId,
block_producer_registration: &BlockProducerRegistration,
registration_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
) -> Result<McTxHash, OffchainError>;
}

impl<T> Register for T
where
T: QueryLedgerState + Transactions + QueryNetwork,
{
async fn register(
&self,
genesis_utxo: UtxoId,
block_producer_registration: &BlockProducerRegistration,
registration_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
) -> Result<McTxHash, OffchainError> {
run_register(
genesis_utxo,
block_producer_registration,
registration_utxo,
payment_signing_key,
self,
)
.await
.map_err(|e| OffchainError::InternalError(e.to_string()))
}
}

pub async fn run_register<C: QueryLedgerState + QueryNetwork + Transactions>(
genesis_utxo: UtxoId,
block_producer_registration: &BlockProducerRegistration,
input_utxo: UtxoId,
payment_signing_key: [u8; 32],
registration_utxo: UtxoId,
payment_signing_key: MainchainPrivateKey,
ogmios_client: &C,
) -> anyhow::Result<McTxHash> {
let ctx = TransactionContext::for_payment_key(payment_signing_key, ogmios_client).await?;
let ctx = TransactionContext::for_payment_key(payment_signing_key.0, ogmios_client).await?;
let own_pkh = ed25519_key_hash_to_mainchain_address_hash(ctx.payment_key_hash());
let validator = crate::scripts_data::registered_candidates_scripts(genesis_utxo)?;
let validator_address = validator.address_bech32(ctx.network)?;
let input_utxo = ctx
let registration_utxo = ctx
.payment_key_utxos
.iter()
.find(|u| u.to_domain() == input_utxo)
.ok_or(anyhow!("input utxo not found at payment address"))?;
.find(|u| u.to_domain() == registration_utxo)
.ok_or(anyhow!("registration utxo not found at payment address"))?;
let all_registration_utxos = ogmios_client.query_utxos(&[validator_address]).await?;
let own_registrations = get_own_registrations(
ed25519_key_hash_to_mainchain_address_hash(ctx.payment_key_hash()),
own_pkh,
block_producer_registration.stake_ownership.pub_key.clone(),
&all_registration_utxos,
)?;
Expand All @@ -52,8 +90,9 @@ pub async fn register<C: QueryLedgerState + QueryNetwork + Transactions>(
let zero_ex_units = ExUnits::new(&0u64.into(), &0u64.into());
let tx = register_tx(
&validator,
own_pkh,
block_producer_registration,
input_utxo,
registration_utxo,
&own_registration_utxos,
&ctx,
zero_ex_units,
Expand All @@ -70,8 +109,9 @@ pub async fn register<C: QueryLedgerState + QueryNetwork + Transactions>(
let validator_redeemer_ex_units = get_first_validator_budget(evaluate_response)?;
let tx = register_tx(
&validator,
own_pkh,
block_producer_registration,
input_utxo,
registration_utxo,
&own_registration_utxos,
&ctx,
validator_redeemer_ex_units,
Expand All @@ -89,18 +129,17 @@ pub async fn register<C: QueryLedgerState + QueryNetwork + Transactions>(
Ok(McTxHash(res.transaction.id))
}


fn ed25519_key_hash_to_mainchain_address_hash(value: Ed25519KeyHash) -> MainchainAddressHash {
// Ed25519KeyHash is represented as [u8;28], same as MainchainAddressHash,
// but it is private and can only be accessed as Vec<u8> so we need to do this
MainchainAddressHash(
value
.to_bytes()
.as_slice()
.try_into()
.expect("impossible: Ed25519KeyHash failed to convert to MainchainAddressHash"),
)
}
fn ed25519_key_hash_to_mainchain_address_hash(value: Ed25519KeyHash) -> MainchainAddressHash {
// Ed25519KeyHash is represented as [u8;28], same as MainchainAddressHash,
// but it is private and can only be accessed as Vec<u8> so we need to do this
MainchainAddressHash(
value
.to_bytes()
.as_slice()
.try_into()
.expect("impossible: Ed25519KeyHash failed to convert to MainchainAddressHash"),
)
}

fn get_own_registrations(
own_pkh: MainchainAddressHash,
Expand All @@ -115,14 +154,16 @@ fn get_own_registrations(
let datum_plutus_data = PlutusData::from_bytes(datum.bytes).map_err(|e| {
anyhow!("Internal error: could not decode datum of validator script: {}", e)
})?;
let block_producer_registration: BlockProducerRegistration =
RegisterValidatorDatum::try_from(datum_plutus_data)
.map_err(|e| {
anyhow!("Internal error: could not decode datum of validator script: {}", e)
})?
.into();
let (own_pkh_from_datum, block_producer_registration): (
MainchainAddressHash,
BlockProducerRegistration,
) = RegisterValidatorDatum::try_from(datum_plutus_data)
.map_err(|e| {
anyhow!("Internal error: could not decode datum of validator script: {}", e)
})?
.into();
if block_producer_registration.stake_ownership.pub_key == spo_pub_key
&& block_producer_registration.own_pkh == own_pkh
&& own_pkh_from_datum == own_pkh
{
own_registrations.push((validator_utxo.clone(), block_producer_registration))
}
Expand All @@ -132,6 +173,7 @@ fn get_own_registrations(

fn register_tx(
validator: &PlutusScript,
own_pkh: MainchainAddressHash,
block_producer_registration: &BlockProducerRegistration,
registration_utxo: &OgmiosUtxo,
own_registration_utxos: &[OgmiosUtxo],
Expand All @@ -155,7 +197,8 @@ fn register_tx(
}

{
let datum = block_producer_registration_to_plutus_data(block_producer_registration);
let datum =
block_producer_registration_to_plutus_data(own_pkh, block_producer_registration);
let amount_builder = TransactionOutputBuilder::new()
.with_address(&validator.address(ctx.network))
.with_plutus_data(&datum)
Expand Down Expand Up @@ -205,6 +248,10 @@ mod tests {
const MIN_UTXO_LOVELACE: u64 = 1000000;
const FIVE_ADA: u64 = 5000000;

fn own_pkh() -> MainchainAddressHash {
MainchainAddressHash([0; 28])
}

fn block_producer_registration(registration_utxo: UtxoId) -> BlockProducerRegistration {
BlockProducerRegistration {
stake_ownership: AdaBasedStaking {
Expand All @@ -214,7 +261,6 @@ mod tests {
sidechain_pub_key: SidechainPublicKey(Vec::new()),
sidechain_signature: SidechainSignature(Vec::new()),
registration_utxo,
own_pkh: MainchainAddressHash([0; 28]),
aura_pub_key: AuraPublicKey(Vec::new()),
grandpa_pub_key: GrandpaPublicKey(Vec::new()),
}
Expand Down Expand Up @@ -254,6 +300,7 @@ mod tests {
block_producer_registration(registration_utxo.to_domain());
let tx = register_tx(
&test_values::test_validator(),
own_pkh(),
&block_producer_registration,
registration_utxo,
&own_registration_utxos,
Expand Down Expand Up @@ -283,7 +330,7 @@ mod tests {
);
assert_eq!(
script_output.plutus_data().unwrap(),
block_producer_registration_to_plutus_data(&block_producer_registration)
block_producer_registration_to_plutus_data(own_pkh(), &block_producer_registration)
);
}

Expand All @@ -305,6 +352,7 @@ mod tests {
};
let tx = register_tx(
&test_values::test_validator(),
own_pkh(),
&block_producer_registration,
registration_utxo,
&own_registration_utxos,
Expand All @@ -319,6 +367,7 @@ mod tests {
fee_is_less_than_one_and_half_ada(&tx);
output_at_validator_has_register_candidate_datum(
&tx,
own_pkh(),
&block_producer_registration,
validator_address,
);
Expand Down Expand Up @@ -362,6 +411,7 @@ mod tests {

fn output_at_validator_has_register_candidate_datum(
tx: &Transaction,
own_pkh: MainchainAddressHash,
block_producer_registration: &BlockProducerRegistration,
validator_address: &Address,
) {
Expand All @@ -370,7 +420,7 @@ mod tests {
outputs.into_iter().find(|o| o.address() == *validator_address).unwrap();
assert_eq!(
validator_output.plutus_data().unwrap(),
block_producer_registration_to_plutus_data(block_producer_registration)
block_producer_registration_to_plutus_data(own_pkh, block_producer_registration)
);
}

Expand Down
Loading

0 comments on commit 09ed96c

Please sign in to comment.