feat(dao): multi-net slot↔ms + live-decode StakeDatum tests
## Multi-net slot↔ms (drops mainnet-only gate) Replaces mainnet_slot_to_posix_ms with slot_to_posix_ms(network, slot) that handles all three Cardano networks. Vote + advance MCP tools no longer hard-error on preprod/preview. Per-network Shelley HF constants (slot, posix_ms_at_slot): - mainnet: 4_492_800 1_596_059_091_000 (2020-07-29 21:44:51 UTC) - preprod: 86_400 1_655_769_600_000 (2022-06-21 00:00 UTC) - preview: 0 1_666_656_000_000 (2022-10-25 00:00 UTC) Pre-Shelley (Byron) slots still rejected — they had different lengths and DAO operations don't need them. Routed via cfg.network at every call site so the MCP tool's behavior follows the active DAO's chain. ## Live-decode StakeDatum regression tests Anchors the StakeDatum type port to two real on-chain UTxOs at the Sulkta stakes_addr: - decodes_sulkta_live_sulkta_stake — 50 Terrapin, owner pkh 84d0..f2f3, utxo d5b73a9d...#0 - decodes_sulkta_live_sulkta_stake — 250 Terrapin, owner pkh c5e3..bfda, utxo 0823a940...#1 Both assert byte-exact CBOR round-trip after decode → to_plutus_data → encode. This is the validator-critical property: the proposal validator does `mkRecordConstr expectedDatum #== outputDatum` on every output, so any drift in field order, integer width, or empty-list shape would silently break vote/cosign/advance txs. Companion to decodes_sulkta_live_proposal_zero in proposal.rs (which covers the 8-field ProposalDatum side).
This commit is contained in:
parent
783f4e5b0e
commit
6f935b58b2
2 changed files with 98 additions and 33 deletions
|
|
@ -341,6 +341,64 @@ mod tests {
|
|||
assert_eq!(StakeDatum::from_plutus_data(&pd).unwrap(), s);
|
||||
}
|
||||
|
||||
/// Decode an actual on-chain stake from the live stakes_addr.
|
||||
/// Anchors the StakeDatum type port to a real UTxO so a future
|
||||
/// encoding refactor can't silently break decode of existing stakes.
|
||||
///
|
||||
/// Source: Koios `address_info` for stakes addr
|
||||
/// `addr1w8msu7psehjlcu5glzjgjtq53m4mk75c9d2p8hfjwyknmfqskkah8`,
|
||||
/// utxo `d5b73a9d1e0fc4cedaf25b1172d379ad36bc39ec8516005cd70b12f9b5bdaa2f#0`.
|
||||
/// Captured 2026-05-06.
|
||||
#[test]
|
||||
fn decodes_live_stake_datum() {
|
||||
use pallas_primitives::PlutusData;
|
||||
let cbor_hex = "9f1832d8799f581c84d08bace7a5f23591d80e91dccd43fa3ea55a8d974f208842b6f2f3ffd87a8080ff";
|
||||
let bytes = hex::decode(cbor_hex).unwrap();
|
||||
let pd: PlutusData = pallas_codec::minicbor::decode(&bytes).unwrap();
|
||||
let stake = StakeDatum::from_plutus_data(&pd).expect("decode first stake");
|
||||
assert_eq!(stake.staked_amount, 50);
|
||||
assert!(matches!(
|
||||
&stake.owner,
|
||||
Credential::PubKey(h) if hex::encode(h) == "84d08bace7a5f23591d80e91dccd43fa3ea55a8d974f208842b6f2f3"
|
||||
));
|
||||
assert!(stake.delegated_to.is_none());
|
||||
assert!(stake.locked_by.is_empty());
|
||||
|
||||
// Round-trip — encoding our decoded stake should give back the
|
||||
// exact bytes we started with. This is the critical property:
|
||||
// any drift in field order, integer encoding, or empty-list
|
||||
// shape would break the validator's bit-exact `==` check on
|
||||
// mutated stake outputs.
|
||||
let re_encoded = pallas_codec::minicbor::to_vec(&stake.to_plutus_data().unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
hex::encode(&re_encoded),
|
||||
cbor_hex,
|
||||
"round-trip CBOR diverged"
|
||||
);
|
||||
}
|
||||
|
||||
/// Same shape as `decodes_live_stake_datum` but for a second
|
||||
/// stake (250 Terrapin). Two-witness regression — catches drift
|
||||
/// even if the first test happens to flatten over a bug.
|
||||
#[test]
|
||||
fn decodes_live_stake_datum_b() {
|
||||
use pallas_primitives::PlutusData;
|
||||
let cbor_hex = "9f18fad8799f581cc5e3425f44c1909b6caab4d80d88aebe85f328bd209eeab03ca2bfdaffd87a8080ff";
|
||||
let bytes = hex::decode(cbor_hex).unwrap();
|
||||
let pd: PlutusData = pallas_codec::minicbor::decode(&bytes).unwrap();
|
||||
let stake = StakeDatum::from_plutus_data(&pd).expect("decode second stake");
|
||||
assert_eq!(stake.staked_amount, 250);
|
||||
assert!(matches!(
|
||||
&stake.owner,
|
||||
Credential::PubKey(h) if hex::encode(h) == "c5e3425f44c1909b6caab4d80d88aebe85f328bd209eeab03ca2bfda"
|
||||
));
|
||||
assert!(stake.delegated_to.is_none());
|
||||
assert!(stake.locked_by.is_empty());
|
||||
|
||||
let re_encoded = pallas_codec::minicbor::to_vec(&stake.to_plutus_data().unwrap()).unwrap();
|
||||
assert_eq!(hex::encode(&re_encoded), cbor_hex, "round-trip CBOR diverged");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stake_redeemer_indices_match_make_is_data_indexed() {
|
||||
let cases = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue