feat(plutus_mint): set language_view per Plutus version + add V2 cost model

Without language_view, pallas does not compute script_data_hash on
the tx body. Plutus txs without script_data_hash get rejected with
ConwayUtxowFailure (PPViewHashesDontMatch SNothing (SJust ...)).

Caught 2026-05-07 attempting governor bootstrap on preprod against
Agora's V2 GST policy. Previous code only set language_view when
the policy was V3 — every V2 mint hit the chain rejection.

Three changes:

1. crates/aldabra-core/src/plutus_cost_models.rs — append
   PLUTUS_V2_COST_MODEL_PREPROD constant (175 i64 entries), pulled
   live from preprod Koios epoch_params 2026-05-07. Same protocol-
   version convention as the existing V3 constant: V2 cost model
   is identical mainnet vs preprod (cost models are protocol-version
   parameters, not network), so the _PREPROD suffix is naming
   convention, not a separation point.

2. crates/aldabra-core/src/plutus_mint.rs — replace the V3-only
   language_view block with a per-PlutusVersion match. V2 wires
   the new constant; V3 keeps the existing
   params.plutus_v3_cost_model path; V1 left as TODO with a note
   (no V1 mint use case yet).

3. crates/aldabra-dao/examples/dump_governor.rs — small cargo
   example that encodes a sample GovernorDatum to CBOR hex via
   the existing aldabra_dao::agora::GovernorDatum::to_plutus_data
   path. Used during preprod DAO bringup to construct the inline
   datum for the governor bootstrap tx. Edit values + re-run for
   any DAO bringup. Builds against the existing pallas-codec
   dev-dependency.
This commit is contained in:
Kayos 2026-05-07 08:52:59 -07:00
parent b50d45b5de
commit ca2f69d28e
3 changed files with 92 additions and 5 deletions

View file

@ -52,3 +52,27 @@ pub const PLUTUS_V3_COST_MODEL_PREPROD: [i64; 297] = [
107490, 3298, 1, 106057, 655, 1, 1964219, 24520,
3,
];
pub const PLUTUS_V2_COST_MODEL_PREPROD: [i64; 175] = [
100788, 420, 1, 1, 1000, 173, 0, 1,
1000, 59957, 4, 1, 11183, 32, 201305, 8356,
4, 16000, 100, 16000, 100, 16000, 100, 16000,
100, 16000, 100, 16000, 100, 100, 100, 16000,
100, 94375, 32, 132994, 32, 61462, 4, 72010,
178, 0, 1, 22151, 32, 91189, 769, 4,
2, 85848, 228465, 122, 0, 1, 1, 1000,
42921, 4, 2, 24548, 29498, 38, 1, 898148,
27279, 1, 51775, 558, 1, 39184, 1000, 60594,
1, 141895, 32, 83150, 32, 15299, 32, 76049,
1, 13169, 4, 22100, 10, 28999, 74, 1,
28999, 74, 1, 43285, 552, 1, 44749, 541,
1, 33852, 32, 68246, 32, 72362, 32, 7243,
32, 7391, 32, 11546, 32, 85848, 228465, 122,
0, 1, 1, 90434, 519, 0, 1, 74433,
32, 85848, 228465, 122, 0, 1, 1, 85848,
228465, 122, 0, 1, 1, 955506, 213312, 0,
2, 270652, 22588, 4, 1457325, 64566, 4, 20467,
1, 4, 0, 141992, 32, 100788, 420, 1,
1, 81663, 32, 59498, 32, 20142, 32, 24588,
32, 20744, 32, 25933, 32, 24623, 32, 43053543,
10, 53384111, 14333, 10, 43574283, 26308, 10,
];

View file

@ -586,11 +586,28 @@ fn prepare_plutus_mint(
.fee(fee)
.network_id(network_id);
// PlutusV3 needs cost-model in script_data_hash. (Mirror of the
// PLUTUS-4 fix in plutus.rs::build_signed_plutus_spend.)
if let Some(cost_model) = params.plutus_v3_cost_model.as_deref() {
if matches!(args.policy_version, PlutusVersion::V3) {
staging = staging.language_view(kind, cost_model.to_vec());
// Plutus V1/V2/V3 each need their cost-model wired via
// language_view so pallas computes script_data_hash on the tx
// body. Without it, chain rejects with PPViewHashesDontMatch.
// Caught 2026-05-07 attempting Agora's V2 GST-policy bootstrap
// mint on preprod — earlier code only set language_view for
// V3 and every V2 mint hit the chain rejection.
match args.policy_version {
PlutusVersion::V2 => {
staging = staging.language_view(
kind,
crate::plutus_cost_models::PLUTUS_V2_COST_MODEL_PREPROD.to_vec(),
);
}
PlutusVersion::V3 => {
if let Some(cost_model) = params.plutus_v3_cost_model.as_deref() {
staging = staging.language_view(kind, cost_model.to_vec());
}
}
PlutusVersion::V1 => {
// V1 cost model not yet provided in aldabra-core. If a
// V1 mint is ever needed, append PLUTUS_V1_COST_MODEL_PREPROD
// to plutus_cost_models.rs and add the matching arm here.
}
}

View file

@ -0,0 +1,46 @@
//! Dump a sample GovernorDatum as PlutusData CBOR hex.
//!
//! Used during preprod DAO bringup (2026-05-07) to construct the
//! inline datum for the governor bootstrap tx. Edit the values in
//! `main()` to your DAO's parameters and run:
//!
//! ```sh
//! cargo run --example dump_governor -p aldabra-dao --release
//! ```
//!
//! Pipe the output hex into `wallet_plutus_mint_unsigned`'s
//! `dest_inline_datum_cbor_hex` arg.
use aldabra_dao::agora::proposal::{ProposalThresholds, ProposalTimingConfig};
use aldabra_dao::agora::GovernorDatum;
use pallas_codec::minicbor;
fn main() {
let g = GovernorDatum {
proposal_thresholds: ProposalThresholds {
execute: 50,
create: 100,
to_voting: 100,
vote: 1,
cosign: 1,
},
next_proposal_id: 0,
proposal_timings: ProposalTimingConfig {
// Short timings for preprod testing — flip these up for
// any real DAO on mainnet (Sulkta uses 7d/7d/48h/24h/1h/30min).
draft_time: 60_000,
voting_time: 60_000,
locking_time: 30_000,
executing_time: 30_000,
min_stake_voting_time: 60_000,
voting_time_range_max_width: 30_000,
},
create_proposal_time_range_max_width: 30_000,
maximum_created_proposals_per_stake: 20,
};
let pd = g.to_plutus_data().expect("encode GovernorDatum");
let mut buf = Vec::new();
minicbor::encode(&pd, &mut buf).expect("encode CBOR");
let hex: String = buf.iter().map(|b| format!("{:02x}", b)).collect();
println!("{}", hex);
}