Skip to content

Commit 38dfd26

Browse files
committed
multi-sig for d-parameter command
1 parent 03f6aac commit 38dfd26

File tree

6 files changed

+102
-36
lines changed

6 files changed

+102
-36
lines changed

changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1.
66

77
## Changed
88

9+
* `upsert-d-parameter` command now works with multi-sig.
910
* `governance update` command now accepts multiple governance authority key hashes, not just one. It also takes `new-governance-threshold` parameter, which is the number of signatures required to perform governance action.
1011
* `governance init` and `governance update` will set Multisig policy implemented with ALeastN Native Script, instead of custom policy implemented as Plutus Script in partner-chains-smart-contracts. This policy doesn't require to set `required_signers` field in the transaction making it more user friendly.
1112
* Extracted the "Ariadne" committee selection algorithm to the `selection` crate.

toolkit/offchain/src/d_param/mod.rs

+84-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
//! The datum encodes D-parameter using VersionedGenericDatum envelope with the D-parameter being
55
//! `datum` field being `[num_permissioned_candidates, num_registered_candidates]`.
66
7+
use std::any::Any;
8+
79
use crate::await_tx::{AwaitTx, FixedDelayRetries};
810
use crate::cardano_keys::CardanoPaymentSigningKey;
911
use crate::csl::{
@@ -20,7 +22,12 @@ use ogmios_client::{
2022
types::OgmiosUtxo,
2123
};
2224
use partner_chains_plutus_data::d_param::{d_parameter_to_plutus_data, DParamDatum};
23-
use sidechain_domain::{DParameter, McTxHash, UtxoId};
25+
use serde_json::json;
26+
use sidechain_domain::{
27+
DParameter, MainchainKeyHash, McSmartContractResult,
28+
McSmartContractResult::{TxCBOR, TxHash},
29+
McTxHash, UtxoId,
30+
};
2431

2532
#[cfg(test)]
2633
mod tests;
@@ -32,7 +39,7 @@ pub trait UpsertDParam {
3239
genesis_utxo: UtxoId,
3340
d_parameter: &DParameter,
3441
payment_signing_key: &CardanoPaymentSigningKey,
35-
) -> anyhow::Result<Option<McTxHash>>;
42+
) -> anyhow::Result<Option<McSmartContractResult>>;
3643
}
3744

3845
impl<C: QueryLedgerState + QueryNetwork + Transactions + QueryUtxoByUtxoId> UpsertDParam for C {
@@ -41,7 +48,7 @@ impl<C: QueryLedgerState + QueryNetwork + Transactions + QueryUtxoByUtxoId> Upse
4148
genesis_utxo: UtxoId,
4249
d_parameter: &DParameter,
4350
payment_signing_key: &CardanoPaymentSigningKey,
44-
) -> anyhow::Result<Option<McTxHash>> {
51+
) -> anyhow::Result<Option<McSmartContractResult>> {
4552
upsert_d_param(
4653
genesis_utxo,
4754
d_parameter,
@@ -62,7 +69,7 @@ pub async fn upsert_d_param<
6269
payment_signing_key: &CardanoPaymentSigningKey,
6370
ogmios_client: &C,
6471
await_tx: &A,
65-
) -> anyhow::Result<Option<McTxHash>> {
72+
) -> anyhow::Result<Option<McSmartContractResult>> {
6673
let ctx = TransactionContext::for_payment_key(payment_signing_key, ogmios_client).await?;
6774
let (validator, policy) = crate::scripts_data::d_parameter_scripts(genesis_utxo, ctx.network)?;
6875
let validator_address = validator.address_bech32(ctx.network)?;
@@ -96,7 +103,7 @@ pub async fn upsert_d_param<
96103
)
97104
},
98105
};
99-
if let Some(tx_hash) = tx_hash_opt {
106+
if let Some(TxHash(tx_hash)) = tx_hash_opt {
100107
await_tx.await_tx_output(ogmios_client, UtxoId::new(tx_hash.0, 0)).await?;
101108
}
102109
Ok(tx_hash_opt)
@@ -131,7 +138,7 @@ async fn insert_d_param<C: QueryLedgerState + Transactions + QueryNetwork>(
131138
ctx: TransactionContext,
132139
genesis_utxo: UtxoId,
133140
client: &C,
134-
) -> anyhow::Result<McTxHash> {
141+
) -> anyhow::Result<McSmartContractResult> {
135142
let gov_data = GovernanceData::get(genesis_utxo, client).await?;
136143

137144
let tx = Costs::calculate_costs(
@@ -140,17 +147,41 @@ async fn insert_d_param<C: QueryLedgerState + Transactions + QueryNetwork>(
140147
)
141148
.await?;
142149

143-
let signed_tx = ctx.sign(&tx).to_bytes();
144-
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
145-
anyhow!(
146-
"Submit insert D-parameter transaction request failed: {}, bytes: {}",
147-
e,
148-
hex::encode(signed_tx)
149-
)
150-
})?;
151-
let tx_id = McTxHash(res.transaction.id);
152-
log::info!("Transaction submitted: {}", hex::encode(tx_id.0));
153-
Ok(tx_id)
150+
if gov_data.policy.is_single_key_policy_for(&ctx.payment_key_hash()) {
151+
let signed_tx = ctx.sign(&tx).to_bytes();
152+
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
153+
anyhow!(
154+
"Submit insert D-parameter transaction request failed: {}, bytes: {}",
155+
e,
156+
hex::encode(signed_tx)
157+
)
158+
})?;
159+
let tx_id = McTxHash(res.transaction.id);
160+
log::info!("Transaction submitted: {}", hex::encode(tx_id.0));
161+
Ok(TxHash(tx_id))
162+
} else {
163+
let authorities = gov_data.policy.key_hashes();
164+
if authorities.contains(&ctx.payment_key_hash().to_bytes().try_into().unwrap()) {
165+
let signed_tx = ctx.sign(&tx).to_bytes();
166+
let tx_envelope = json!(
167+
{ "type": "Witnessed Tx ConwayEra",
168+
"description": "",
169+
"cborHex": hex::encode(signed_tx.clone())
170+
}
171+
);
172+
log::info!("Transaction envelope: {}", tx_envelope);
173+
Ok(TxCBOR(signed_tx))
174+
} else {
175+
let tx_envelope = json!(
176+
{ "type": "Unwitnessed Tx ConwayEra",
177+
"description": "",
178+
"cborHex": hex::encode(tx.to_bytes())
179+
}
180+
);
181+
log::info!("Transaction envelope: {}", tx_envelope);
182+
Ok(TxCBOR(tx.to_bytes()))
183+
}
184+
}
154185
}
155186

156187
async fn update_d_param<C: QueryLedgerState + Transactions + QueryNetwork>(
@@ -161,7 +192,7 @@ async fn update_d_param<C: QueryLedgerState + Transactions + QueryNetwork>(
161192
ctx: TransactionContext,
162193
genesis_utxo: UtxoId,
163194
client: &C,
164-
) -> anyhow::Result<McTxHash> {
195+
) -> anyhow::Result<McSmartContractResult> {
165196
let governance_data = GovernanceData::get(genesis_utxo, client).await?;
166197

167198
let tx = Costs::calculate_costs(
@@ -180,17 +211,41 @@ async fn update_d_param<C: QueryLedgerState + Transactions + QueryNetwork>(
180211
)
181212
.await?;
182213

183-
let signed_tx = ctx.sign(&tx).to_bytes();
184-
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
185-
anyhow!(
186-
"Submit D-parameter update transaction request failed: {}, bytes: {}",
187-
e,
188-
hex::encode(signed_tx)
189-
)
190-
})?;
191-
let tx_id = McTxHash(res.transaction.id);
192-
log::info!("Update D-parameter transaction submitted: {}", hex::encode(tx_id.0));
193-
Ok(tx_id)
214+
if governance_data.policy.is_single_key_policy_for(&ctx.payment_key_hash()) {
215+
let signed_tx = ctx.sign(&tx).to_bytes();
216+
let res = client.submit_transaction(&signed_tx).await.map_err(|e| {
217+
anyhow!(
218+
"Submit D-parameter update transaction request failed: {}, bytes: {}",
219+
e,
220+
hex::encode(signed_tx)
221+
)
222+
})?;
223+
let tx_id = McTxHash(res.transaction.id);
224+
log::info!("Update D-parameter transaction submitted: {}", hex::encode(tx_id.0));
225+
Ok(TxHash(tx_id))
226+
} else {
227+
let authorities = governance_data.policy.key_hashes();
228+
if authorities.contains(&ctx.payment_key_hash().to_bytes().try_into().unwrap()) {
229+
let signed_tx = ctx.sign(&tx).to_bytes();
230+
let tx_envelope = json!(
231+
{ "type": "Witnessed Tx ConwayEra",
232+
"description": "",
233+
"cborHex": hex::encode(signed_tx.clone())
234+
}
235+
);
236+
log::info!("Transaction envelope: {}", tx_envelope);
237+
Ok(TxCBOR(signed_tx))
238+
} else {
239+
let tx_envelope = json!(
240+
{ "type": "Unwitnessed Tx ConwayEra",
241+
"description": "",
242+
"cborHex": hex::encode(tx.to_bytes())
243+
}
244+
);
245+
log::info!("Transaction envelope: {}", tx_envelope);
246+
Ok(TxCBOR(tx.to_bytes()))
247+
}
248+
}
194249
}
195250

196251
fn mint_d_param_token_tx(

toolkit/offchain/src/governance.rs

+8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ impl GovernancePolicyScript {
5252
},
5353
}
5454
}
55+
56+
/// Returns the list of authorities for the policy
57+
pub(crate) fn key_hashes(&self) -> Vec<[u8; 28]> {
58+
match self {
59+
Self::MultiSig(PartnerChainsMultisigPolicy { key_hashes, .. }) => key_hashes.clone(),
60+
Self::AtLeastNNativeScript(SimpleAtLeastN { key_hashes, .. }) => key_hashes.clone(),
61+
}
62+
}
5563
}
5664

5765
/// Plutus MultiSig smart contract implemented in partner-chains-smart-contracts repo

toolkit/offchain/tests/integration_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ async fn run_upsert_d_param<
332332
num_registered_candidates: u16,
333333
pkey: &CardanoPaymentSigningKey,
334334
client: &T,
335-
) -> Option<McTxHash> {
335+
) -> Option<McSmartContractResult> {
336336
d_param::upsert_d_param(
337337
genesis_utxo,
338338
&DParameter { num_permissioned_candidates, num_registered_candidates },

toolkit/partner-chains-cli/src/setup_main_chain_state/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ fn no_ariadne_parameters_on_main_chain_do_updates() {
4242
UtxoId::default(),
4343
new_d_parameter(),
4444
payment_signing_key(),
45-
Ok(Some(McTxHash([1; 32]))),
45+
Ok(Some(McSmartContractResult::TxHash(McTxHash([1; 32])))),
4646
)
4747
.with_upsert_permissioned_candidates(
4848
genesis_utxo(),
@@ -96,7 +96,7 @@ fn ariadne_parameters_are_on_main_chain_do_update() {
9696
UtxoId::default(),
9797
new_d_parameter(),
9898
payment_signing_key(),
99-
Ok(Some(McTxHash([1; 32]))),
99+
Ok(Some(McSmartContractResult::TxHash(McTxHash([1; 32])))),
100100
)
101101
.with_upsert_permissioned_candidates(
102102
genesis_utxo(),

toolkit/partner-chains-cli/src/tests/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,10 @@ pub struct OffchainMock {
235235
pub scripts_data: HashMap<UtxoId, Result<ScriptsData, OffchainError>>,
236236
pub init_governance:
237237
HashMap<(UtxoId, MainchainKeyHash, PrivateKeyBytes), Result<OgmiosTx, OffchainError>>,
238-
pub upsert_d_param:
239-
HashMap<(UtxoId, DParameter, PrivateKeyBytes), Result<Option<McTxHash>, String>>,
238+
pub upsert_d_param: HashMap<
239+
(UtxoId, DParameter, PrivateKeyBytes),
240+
Result<Option<McSmartContractResult>, String>,
241+
>,
240242
pub upsert_permissioned_candidates: HashMap<
241243
(UtxoId, Vec<sidechain_domain::PermissionedCandidateData>, PrivateKeyBytes),
242244
Result<Option<McSmartContractResult>, String>,
@@ -286,7 +288,7 @@ impl OffchainMock {
286288
genesis_utxo: UtxoId,
287289
d_param: DParameter,
288290
payment_key: PrivateKeyBytes,
289-
result: Result<Option<McTxHash>, String>,
291+
result: Result<Option<McSmartContractResult>, String>,
290292
) -> Self {
291293
Self { upsert_d_param: [((genesis_utxo, d_param, payment_key), result)].into(), ..self }
292294
}
@@ -366,7 +368,7 @@ impl UpsertDParam for OffchainMock {
366368
genesis_utxo: UtxoId,
367369
d_parameter: &DParameter,
368370
payment_signing_key: &CardanoPaymentSigningKey,
369-
) -> anyhow::Result<Option<McTxHash>> {
371+
) -> anyhow::Result<Option<McSmartContractResult>> {
370372
self.upsert_d_param
371373
.get(&(genesis_utxo, d_parameter.clone(), payment_signing_key.to_bytes()))
372374
.cloned()

0 commit comments

Comments
 (0)