pallas-txbuilder: thread certificates through StagingTransaction → Conway build
second of the upstream TODOs. addresses the `// pub certificates: TODO` comment in transaction/model.rs and the `certificates: None, // TODO` in conway.rs:243. implementation: - new field: pub certificates: Option<Vec<Vec<u8>>> on StagingTransaction. cbor bytes per cert (each entry is conway::Certificate) — same opaque- bytes pattern as auxiliary_data, for the same reason (Certificate doesn't impl Eq). - builder: .add_certificate(cbor_bytes) and .clear_certificates(). - conway::build_conway_raw decodes each entry via Certificate::decode_fragment + plumbs into TransactionBody.certificates via NonEmptySet::from_vec. fragment-decode failures map to TxBuilderError::CorruptedTxBytes (same as aux_data path). tests: - certificates_plumb_through_to_tx_body: encodes a StakeRegistration(AddrKeyhash([7;28])), attaches, builds, decodes resulting tx, asserts the cert round-trips byte-for-byte. - no_certificates_means_none: confirms unset path keeps body.certificates None (no spurious empty Set). unblocks aldabra phase 4.6 stake delegation. PR upstream still pending; this is part of the same vendored fork as the auxiliary_data work.
This commit is contained in:
parent
51a0d0bd77
commit
68221fbcb5
3 changed files with 87 additions and 7 deletions
|
|
@ -4,10 +4,10 @@ use pallas_codec::utils::CborWrap;
|
|||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::{
|
||||
conway::{
|
||||
AuxiliaryData, DatumOption, ExUnits as PallasExUnits, NativeScript, NetworkId, NonZeroInt,
|
||||
PlutusData, PlutusScript, PostAlonzoTransactionOutput, PseudoScript as PallasScript,
|
||||
PseudoTransactionOutput, Redeemer, RedeemerTag, TransactionBody, TransactionInput, Tx,
|
||||
Value, WitnessSet,
|
||||
AuxiliaryData, Certificate, DatumOption, ExUnits as PallasExUnits, NativeScript, NetworkId,
|
||||
NonZeroInt, PlutusData, PlutusScript, PostAlonzoTransactionOutput,
|
||||
PseudoScript as PallasScript, PseudoTransactionOutput, Redeemer, RedeemerTag,
|
||||
TransactionBody, TransactionInput, Tx, Value, WitnessSet,
|
||||
},
|
||||
Fragment, NonEmptyKeyValuePairs, NonEmptySet, PositiveCoin,
|
||||
};
|
||||
|
|
@ -240,8 +240,18 @@ impl BuildConway for StagingTransaction {
|
|||
ttl: self.invalid_from_slot,
|
||||
validity_interval_start: self.valid_from_slot,
|
||||
fee: self.fee.unwrap_or_default(),
|
||||
certificates: None, // TODO
|
||||
withdrawals: None, // TODO
|
||||
certificates: NonEmptySet::from_vec(
|
||||
self.certificates
|
||||
.as_deref()
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.map(|bytes| {
|
||||
Certificate::decode_fragment(bytes)
|
||||
.map_err(|_| TxBuilderError::CorruptedTxBytes)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
),
|
||||
withdrawals: None, // TODO
|
||||
auxiliary_data_hash: None, // TODO (accept user input)
|
||||
mint,
|
||||
script_data_hash,
|
||||
|
|
@ -439,6 +449,51 @@ mod tests {
|
|||
assert_eq!(round_tripped_bytes, original_again);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn certificates_plumb_through_to_tx_body() {
|
||||
use pallas_primitives::conway::{Certificate, StakeCredential};
|
||||
use pallas_crypto::hash::Hash;
|
||||
|
||||
// A trivial stake-registration cert.
|
||||
let stake_pkh = Hash::<28>::from([7u8; 28]);
|
||||
let cert = Certificate::StakeRegistration(StakeCredential::AddrKeyhash(stake_pkh));
|
||||
let cert_bytes = minicbor::to_vec(&cert).expect("encode cert");
|
||||
|
||||
let tx = StagingTransaction::new()
|
||||
.add_certificate(cert_bytes)
|
||||
.network_id(0)
|
||||
.fee(180_000)
|
||||
.build_conway_raw()
|
||||
.expect("build_conway_raw");
|
||||
|
||||
let body = pallas_primitives::conway::Tx::decode_fragment(tx.tx_bytes.as_ref())
|
||||
.expect("decode tx_bytes");
|
||||
let certs = body
|
||||
.transaction_body
|
||||
.certificates
|
||||
.expect("certificates field populated");
|
||||
let certs_vec: Vec<_> = certs.to_vec();
|
||||
assert_eq!(certs_vec.len(), 1);
|
||||
match &certs_vec[0] {
|
||||
Certificate::StakeRegistration(StakeCredential::AddrKeyhash(h)) => {
|
||||
assert_eq!(h.as_ref(), &[7u8; 28]);
|
||||
}
|
||||
other => panic!("expected StakeRegistration, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_certificates_means_none() {
|
||||
let tx = StagingTransaction::new()
|
||||
.network_id(0)
|
||||
.fee(180_000)
|
||||
.build_conway_raw()
|
||||
.expect("build_conway_raw");
|
||||
let body = pallas_primitives::conway::Tx::decode_fragment(tx.tx_bytes.as_ref())
|
||||
.expect("decode tx_bytes");
|
||||
assert!(body.transaction_body.certificates.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_auxiliary_data_means_no_hash() {
|
||||
let tx = StagingTransaction::new()
|
||||
|
|
|
|||
|
|
@ -45,7 +45,11 @@ pub struct StagingTransaction {
|
|||
/// doesn't implement `Eq`, which the rest of this struct requires.
|
||||
/// `conway::build_conway_raw` decodes on the way out.
|
||||
pub auxiliary_data: Option<Vec<u8>>,
|
||||
// pub certificates: TODO
|
||||
/// CBOR-encoded `pallas_primitives::conway::Certificate` entries,
|
||||
/// in tx order. `conway::build_conway_raw` decodes each one and
|
||||
/// plumbs them into `TransactionBody.certificates`. Used for
|
||||
/// stake registration/delegation, pool registration, DRep ops, etc.
|
||||
pub certificates: Option<Vec<Vec<u8>>>,
|
||||
// pub withdrawals: TODO
|
||||
// pub updates: TODO
|
||||
// pub phase_2_valid: TODO
|
||||
|
|
@ -396,6 +400,26 @@ impl StagingTransaction {
|
|||
self.auxiliary_data = None;
|
||||
self
|
||||
}
|
||||
|
||||
/// Append a CBOR-encoded `conway::Certificate` to the tx body.
|
||||
/// Common cases: stake registration, stake delegation, DRep
|
||||
/// registration, pool retirement.
|
||||
///
|
||||
/// Encode the typed `Certificate` via
|
||||
/// `pallas_codec::minicbor::to_vec(&cert)` or
|
||||
/// `Fragment::encode_fragment` and pass the bytes here.
|
||||
pub fn add_certificate(mut self, cbor_bytes: Vec<u8>) -> Self {
|
||||
let mut certs = self.certificates.unwrap_or_default();
|
||||
certs.push(cbor_bytes);
|
||||
self.certificates = Some(certs);
|
||||
self
|
||||
}
|
||||
|
||||
/// Drop any certificates previously attached.
|
||||
pub fn clear_certificates(mut self) -> Self {
|
||||
self.certificates = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Don't want our wrapper types in fields public
|
||||
|
|
|
|||
|
|
@ -481,6 +481,7 @@ mod tests {
|
|||
script_data_hash: Some(Bytes32([0; 32])),
|
||||
language_view: Some(crate::scriptdata::LanguageView(1, vec![1, 2, 3])),
|
||||
auxiliary_data: None,
|
||||
certificates: None,
|
||||
};
|
||||
|
||||
let serialised_tx = serde_json::to_string(&tx).unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue