From aa99f111e310cb63eb86829da581beeff56944f1 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Wed, 24 Jul 2024 03:19:02 +1000 Subject: [PATCH 01/15] Initial impl of node error decoding for local-tx-submission --- examples/crawler/src/main.rs | 15 +- examples/n2c-miniprotocols/src/main.rs | 63 +- pallas-codec/Cargo.toml | 1 + pallas-network/Cargo.toml | 4 +- pallas-network/src/facades.rs | 20 +- .../localtxsubmission/cardano_node_errors.rs | 1311 +++++++++++++++++ .../miniprotocols/localtxsubmission/client.rs | 181 ++- .../miniprotocols/localtxsubmission/codec.rs | 137 +- .../miniprotocols/localtxsubmission/mod.rs | 1 + .../localtxsubmission/protocol.rs | 3 +- .../test_resources/complete_script_error.txt | 1 + pallas-network/tests/protocols.rs | 5 +- 12 files changed, 1638 insertions(+), 104 deletions(-) create mode 100644 pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs create mode 100644 pallas-network/test_resources/complete_script_error.txt diff --git a/examples/crawler/src/main.rs b/examples/crawler/src/main.rs index 2e5d9ab7..78e85d9d 100644 --- a/examples/crawler/src/main.rs +++ b/examples/crawler/src/main.rs @@ -6,7 +6,10 @@ use pallas::{ ledger::traverse::{MultiEraBlock, MultiEraTx}, network::{ facades::NodeClient, - miniprotocols::{chainsync::NextResponse, Point}, + miniprotocols::{ + chainsync::NextResponse, localtxsubmission::cardano_node_errors::NodeErrorDecoder, + Point, + }, }, }; @@ -28,9 +31,13 @@ async fn main() -> Result<()> { let args = Args::parse(); // Connect to the local node over the file socket - let mut client = NodeClient::connect(args.socket_path.clone(), args.network_magic) - .await - .unwrap(); + let mut client = NodeClient::connect( + args.socket_path.clone(), + args.network_magic, + NodeErrorDecoder::default(), + ) + .await + .unwrap(); // Find an intersection point using the points on the command line // The response would tell us what point we found, and what the current tip is diff --git a/examples/n2c-miniprotocols/src/main.rs b/examples/n2c-miniprotocols/src/main.rs index de784c37..ab251af1 100644 --- a/examples/n2c-miniprotocols/src/main.rs +++ b/examples/n2c-miniprotocols/src/main.rs @@ -8,35 +8,46 @@ use pallas::{ miniprotocols::{ chainsync, localstate::queries_v16::{self, Addr, Addrs}, + localtxsubmission::cardano_node_errors::NodeErrorDecoder, Point, PRE_PRODUCTION_MAGIC, }, }, }; use tracing::info; -async fn do_localstate_query(client: &mut NodeClient) { - let client = client.statequery(); +async fn do_localstate_query<'a>( + mut client: NodeClient<'a, NodeErrorDecoder>, +) -> NodeClient<'a, NodeErrorDecoder> { + let localstate_client = client.statequery(); - client.acquire(None).await.unwrap(); + localstate_client.acquire(None).await.unwrap(); - let result = queries_v16::get_chain_point(client).await.unwrap(); + let result = queries_v16::get_chain_point(localstate_client) + .await + .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_system_start(client).await.unwrap(); + let result = queries_v16::get_system_start(localstate_client) + .await + .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_chain_block_no(client).await.unwrap(); + let result = queries_v16::get_chain_block_no(localstate_client) + .await + .unwrap(); info!("result: {:?}", result); - let era = queries_v16::get_current_era(client).await.unwrap(); + let era = queries_v16::get_current_era(localstate_client) + .await + .unwrap(); info!("result: {:?}", era); - let result = queries_v16::get_block_epoch_number(client, era) + let result = queries_v16::get_block_epoch_number(localstate_client, era) .await .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_stake_distribution(client, era) + let result = queries_v16::get_stake_distribution(localstate_client, era) .await .unwrap(); info!("result: {:?}", result); @@ -52,36 +63,43 @@ async fn do_localstate_query(client: &mut NodeClient) { let addry: Addr = addry.to_vec().into(); let addrs: Addrs = vec![addrx, addry]; - let result = queries_v16::get_utxo_by_address(client, era, addrs) + let result = queries_v16::get_utxo_by_address(localstate_client, era, addrs) .await .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_current_pparams(client, era).await.unwrap(); + let result = queries_v16::get_current_pparams(localstate_client, era) + .await + .unwrap(); println!("result: {:?}", result); // Stake pool ID/verification key hash (either Bech32-decoded or hex-decoded). // Empty Set means all pools. let pools: BTreeSet = BTreeSet::new(); - let result = queries_v16::get_stake_snapshots(client, era, pools) + let result = queries_v16::get_stake_snapshots(localstate_client, era, pools) .await .unwrap(); println!("result: {:?}", result); - let result = queries_v16::get_genesis_config(client, era).await.unwrap(); + let result = queries_v16::get_genesis_config(localstate_client, era) + .await + .unwrap(); println!("result: {:?}", result); // Ensure decoding across version disparities by always receiving a valid // response using the wrap function for the query result with CBOR-in-CBOR // concept. let query = queries_v16::BlockQuery::GetCurrentPParams; - let result = queries_v16::get_cbor(client, era, query).await.unwrap(); + let result = queries_v16::get_cbor(localstate_client, era, query) + .await + .unwrap(); println!("result: {:?}", result); - client.send_release().await.unwrap(); + localstate_client.send_release().await.unwrap(); + client } -async fn do_chainsync(client: &mut NodeClient) { +async fn do_chainsync<'a>(client: &'a mut NodeClient<'a, NodeErrorDecoder>) { let known_points = vec![Point::Specific( 43847831u64, hex::decode("15b9eeee849dd6386d3770b0745e0450190f7560e5159b1b3ab13b14b2684a45").unwrap(), @@ -125,12 +143,17 @@ async fn main() { // we connect to the unix socket of the local node. Make sure you have the right // path for your environment - let mut client = NodeClient::connect(SOCKET_PATH, PRE_PRODUCTION_MAGIC) - .await - .unwrap(); + let client = NodeClient::connect( + SOCKET_PATH, + PRE_PRODUCTION_MAGIC, + NodeErrorDecoder::default(), + ) + .await + .unwrap(); // execute an arbitrary "Local State" query against the node - do_localstate_query(&mut client).await; + + let mut client = do_localstate_query(client).await; // execute the chainsync flow from an arbitrary point in the chain do_chainsync(&mut client).await; diff --git a/pallas-codec/Cargo.toml b/pallas-codec/Cargo.toml index 0ec01011..9d1d2e89 100644 --- a/pallas-codec/Cargo.toml +++ b/pallas-codec/Cargo.toml @@ -16,6 +16,7 @@ authors = [ [features] default = [] +half = ["minicbor/half"] [dependencies] hex = "0.4.3" diff --git a/pallas-network/Cargo.toml b/pallas-network/Cargo.toml index a8a7c696..a0602b66 100644 --- a/pallas-network/Cargo.toml +++ b/pallas-network/Cargo.toml @@ -14,8 +14,10 @@ authors = ["Santiago Carmuega ", "Pi Lanningham { plexer: RunningPlexer, handshake: handshake::N2CClient, chainsync: chainsync::N2CClient, statequery: localstate::Client, - submission: localtxsubmission::Client, + submission: localtxsubmission::Client<'a, ErrDecoder>, monitor: txmonitor::Client, } -impl NodeClient { - pub fn new(bearer: Bearer) -> Self { +impl<'a, ErrDecoder> NodeClient<'a, ErrDecoder> { + pub fn new(bearer: Bearer, local_tx_error_decoder: ErrDecoder) -> Self { let mut plexer = multiplexer::Plexer::new(bearer); let hs_channel = plexer.subscribe_client(PROTOCOL_N2C_HANDSHAKE); @@ -307,18 +307,22 @@ impl NodeClient { handshake: handshake::Client::new(hs_channel), chainsync: chainsync::Client::new(cs_channel), statequery: localstate::Client::new(sq_channel), - submission: localtxsubmission::Client::new(tx_channel), + submission: localtxsubmission::Client::new(tx_channel, local_tx_error_decoder), monitor: txmonitor::Client::new(mo_channel), } } #[cfg(unix)] - pub async fn connect(path: impl AsRef, magic: u64) -> Result { + pub async fn connect( + path: impl AsRef, + magic: u64, + err_decoder: ErrDecoder, + ) -> Result { let bearer = Bearer::connect_unix(path) .await .map_err(Error::ConnectFailure)?; - let mut client = Self::new(bearer); + let mut client = Self::new(bearer, err_decoder); let versions = handshake::n2c::VersionTable::v10_and_above(magic); @@ -413,7 +417,7 @@ impl NodeClient { &mut self.statequery } - pub fn submission(&mut self) -> &mut localtxsubmission::Client { + pub fn submission(&'a mut self) -> &mut localtxsubmission::Client<'a, ErrDecoder> { &mut self.submission } diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs new file mode 100644 index 00000000..3241f37d --- /dev/null +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -0,0 +1,1311 @@ +use pallas_codec::minicbor::{ + self, + data::{Int, Type}, + decode::{self, Error, Token}, + Decode, Decoder, +}; +use pallas_primitives::conway::ScriptHash; +use pallas_utxorpc::TxHash; + +use crate::miniprotocols::localtxsubmission::codec::DecodingResult; + +use super::codec::DecodeCBORSplitPayload; + +/// Decodes Cardano node errors whose CBOR byte representation could be split over multiple +/// payloads. +pub struct NodeErrorDecoder { + /// When decoding the error responses of the node, we use a stack to track the location of the + /// decoding relative to an outer scope (most often a definite array). We need it because if we + /// come across an error that we cannot handle, we must still consume all the CBOR bytes that + /// represent this error. + pub context_stack: Vec, + /// Response bytes from the cardano node. Note that there are payload limits and so the bytes + /// may be truncated. + pub response_bytes: Vec, + /// This field is used to determine if there are still CBOR bytes that have yet to be decoded. + /// + /// It has a value of 0 if decoding has not yet started. Otherwise it takes the value of the + /// index in `response_bytes` that is also pointed to by the minicbor decoder after a + /// _successful_ decoding of a `TxApplyErrors` instance. + pub ix_start_unprocessed_bytes: usize, + /// This field is true if the current decoding of a `TXApplyErrors` instance is complete, which + /// only happens once the CBOR BREAK token is decoded to terminate the indefinite array which is + /// part of the `TxApplyErrors` encoded structure. + pub cbor_break_token_seen: bool, +} + +impl NodeErrorDecoder { + pub fn new() -> Self { + Self { + context_stack: vec![], + response_bytes: vec![], + ix_start_unprocessed_bytes: 0, + cbor_break_token_seen: false, + } + } +} + +impl Default for NodeErrorDecoder { + fn default() -> Self { + Self::new() + } +} + +impl DecodeCBORSplitPayload for NodeErrorDecoder { + type Entity = Vec; + + fn try_decode_with_new_bytes( + &mut self, + bytes: &[u8], + ) -> Result, decode::Error> { + self.response_bytes.extend_from_slice(bytes); + let bytes = self.response_bytes.clone(); + let mut decoder = Decoder::new(&bytes); + let mut errors = vec![]; + + loop { + match TxApplyErrors::decode(&mut decoder, self) { + Ok(tx_err) => { + errors.push(tx_err); + } + Err(e) => { + if !e.is_end_of_input() { + return Err(e); + } else { + break; + } + } + } + } + + if self.has_undecoded_bytes() { + Ok(DecodingResult::Incomplete(errors)) + } else { + Ok(DecodingResult::Complete(errors)) + } + } + + fn has_undecoded_bytes(&self) -> bool { + self.ix_start_unprocessed_bytes + 1 < self.response_bytes.len() + } +} + +#[derive(Debug)] +pub struct TxApplyErrors { + pub non_script_errors: Vec, +} + +impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + let mut non_script_errors = vec![]; + + let mut probe = d.probe(); + if let Err(e) = next_token(&mut probe) { + if e.is_end_of_input() { + return Err(e); + } + } + + println!( + "1111111, buf_len: {}, position: {}", + d.input().len(), + d.position() + ); + expect_definite_array(vec![2], d, ctx)?; + println!("2222222"); + let tag = expect_u8(d, ctx)?; + assert_eq!(tag, 2); + expect_definite_array(vec![1], d, ctx)?; + expect_definite_array(vec![2], d, ctx)?; + + // This tag is not totally understood (could represent the Cardano era). + let _inner_tag = expect_u8(d, ctx)?; + + // Here we expect an indefinite array + expect_indefinite_array(d, ctx)?; + while let Ok(t) = d.datatype() { + println!("type: {:?}", t); + if let Type::Break = t { + // Here we have a clean decoding of TXApplyErrors + d.skip()?; + ctx.ix_start_unprocessed_bytes = d.position(); + ctx.cbor_break_token_seen = false; + return Ok(Self { non_script_errors }); + } + + match ShelleyLedgerPredFailure::decode(d, ctx) { + Ok(err) => { + assert!(ctx.context_stack.is_empty()); + non_script_errors.push(err); + + // On successful decoding, there may be another such error to decode, so we'll + // iterate again. + } + Err(e) => { + if ctx.cbor_break_token_seen { + // If decoding failed but the CBOR break token for indefinite array has been + // seen, it means that a complete instance of `TxApplyErrors` has been + // decoded. + ctx.ix_start_unprocessed_bytes = d.position(); + ctx.cbor_break_token_seen = false; + return Ok(Self { non_script_errors }); + } else if e.is_end_of_input() { + //return Err(Error::message("TxApplyErrors::decode: Not enough bytes")); + return Err(e); + } + + // Failed to decode ShelleyLedgerPredFailure, but more bytes remain, so continue + // processing. + } + } + } + + unreachable!() + } +} + +#[derive(Debug)] +/// Top level type for ledger errors +pub enum ShelleyLedgerPredFailure { + UtxowFailure(BabbageUtxowPredFailure), + DelegsFailure, +} + +impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + if let Err(e) = expect_definite_array(vec![2], d, ctx) { + if e.is_end_of_input() { + return Err(e); + } + clear_unknown_entity(d, &mut ctx.context_stack)?; + } + println!( + "ShelleyLedgerPredFailure::decode inside: CTX {:?}", + ctx.context_stack + ); + match expect_u8(d, ctx) { + Ok(tag) => match tag { + 0 => match BabbageUtxowPredFailure::decode(d, ctx) { + Ok(utxow_failure) => Ok(ShelleyLedgerPredFailure::UtxowFailure(utxow_failure)), + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + clear_unknown_entity(d, &mut ctx.context_stack)?; + Err(e) + } + } + }, + _ => { + clear_unknown_entity(d, &mut ctx.context_stack)?; + Err(Error::message("not ShelleyLedgerPredFailure")) + } + }, + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + clear_unknown_entity(d, &mut ctx.context_stack)?; + Err(Error::message( + "ShelleyLedgerPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[allow(clippy::enum_variant_names)] +#[derive(Debug)] +pub enum BabbageUtxowPredFailure { + AlonzoInBabbageUtxowPredFailure(AlonzoUtxowPredFailure), + UtxoFailure(BabbageUtxoPredFailure), + MalformedScriptWitnesses, + MalformedReferenceScripts, +} + +impl Decode<'_, NodeErrorDecoder> for BabbageUtxowPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => match tag { + 1 => { + let utxo_failure = AlonzoUtxowPredFailure::decode(d, ctx)?; + Ok(BabbageUtxowPredFailure::AlonzoInBabbageUtxowPredFailure( + utxo_failure, + )) + } + 2 => { + let utxo_failure = BabbageUtxoPredFailure::decode(d, ctx)?; + Ok(BabbageUtxowPredFailure::UtxoFailure(utxo_failure)) + } + _ => Err(Error::message("not BabbageUtxowPredFailure")), + }, + + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "BabbageUtxowPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[allow(clippy::enum_variant_names)] +#[derive(Debug)] +pub enum BabbageUtxoPredFailure { + AlonzoInBabbageUtxoPredFailure(AlonzoUtxoPredFailure), + IncorrectTotalCollateralField, + BabbageOutputTooSmallUTxO, + BabbageNonDisjointRefInputs, +} + +impl Decode<'_, NodeErrorDecoder> for BabbageUtxoPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => match tag { + 1 => { + let alonzo_failure = AlonzoUtxoPredFailure::decode(d, ctx)?; + Ok(BabbageUtxoPredFailure::AlonzoInBabbageUtxoPredFailure( + alonzo_failure, + )) + } + _ => Err(Error::message("not BabbageUtxoPredFailure")), + }, + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "BabbageUtxoPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub enum AlonzoUtxoPredFailure { + BadInputsUtxo(Vec), + OutsideValidityIntervalUTxO, + MaxTxSizeUTxO, + InputSetEmptyUTxO, + FeeTooSmallUTxO, + ValueNotConservedUTxO { + consumed_value: pallas_primitives::conway::Value, + produced_value: pallas_primitives::conway::Value, + }, + WrongNetwork, + WrongNetworkWithdrawal, + OutputTooSmallUTxO, + UtxosFailure(AlonzoUtxosPredFailure), + OutputBootAddrAttrsTooBig, + TriesToForgeADA, + OutputTooBigUTxO, + InsufficientCollateral, + ScriptsNotPaidUTxO, + ExUnitsTooBigUTxO, + CollateralContainsNonADA, + WrongNetworkInTxBody, + OutsideForecast, + TooManyCollateralInputs, + NoCollateralInputs, +} + +impl Decode<'_, NodeErrorDecoder> for AlonzoUtxoPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + let arr_len = expect_definite_array(vec![2, 3], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => { + match tag { + 0 if arr_len == 2 => { + // BadInputsUtxo + if let Some(num_bad_inputs) = d.array()? { + let mut bad_inputs = vec![]; + for _ in 0..num_bad_inputs { + let tx_input = TxInput::decode(d, ctx)?; + bad_inputs.push(tx_input); + } + Ok(AlonzoUtxoPredFailure::BadInputsUtxo(bad_inputs)) + } else { + Err(Error::message("expected array of tx inputs")) + } + } + 5 if arr_len == 3 => { + // ValueNotConservedUtxo + + let consumed_value = decode_conway_value(d, ctx)?; + let produced_value = decode_conway_value(d, ctx)?; + + Ok(AlonzoUtxoPredFailure::ValueNotConservedUTxO { + consumed_value, + produced_value, + }) + } + 7 if arr_len == 2 => { + // UTXOS failure (currently handle just script errors) + let utxos_failure = AlonzoUtxosPredFailure::decode(d, ctx)?; + Ok(AlonzoUtxoPredFailure::UtxosFailure(utxos_failure)) + } + _ => Err(Error::message("not AlonzoUtxoPredFailure")), + } + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "AlonzoUtxoPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub enum AlonzoUtxosPredFailure { + ValidationTagMismatch { + is_valid: bool, + description: TagMismatchDescription, + }, + CollectErrors, + UpdateFailure, +} + +impl Decode<'_, NodeErrorDecoder> for AlonzoUtxosPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + let arr_len = expect_definite_array(vec![2, 3], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => match tag { + 0 => { + if arr_len == 3 { + let is_valid = expect_bool(d, ctx)?; + let description = TagMismatchDescription::decode(d, ctx)?; + Ok(AlonzoUtxosPredFailure::ValidationTagMismatch { + is_valid, + description, + }) + } else { + Err(Error::message( + "AlonzoUtxosPredFailure::decode: expected array(3) for `ValidationTagMismatch`", + )) + } + } + _ => Err(Error::message(format!( + "AlonzoUtxosPredFailure::decode: unknown tag: {}", + tag + ))), + }, + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "AlonzoUtxosPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub enum TagMismatchDescription { + PassUnexpectedly, + FailUnexpectedly(Vec), +} + +impl Decode<'_, NodeErrorDecoder> for TagMismatchDescription { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => match tag { + 0 => Ok(TagMismatchDescription::PassUnexpectedly), + 1 => { + let num_failures = expect_definite_array(vec![], d, ctx)?; + let mut failures = Vec::with_capacity(num_failures as usize); + for _ in 0..num_failures { + let description = FailureDescription::decode(d, ctx)?; + failures.push(description); + } + Ok(TagMismatchDescription::FailUnexpectedly(failures)) + } + _ => Err(Error::message(format!( + "TagMismatchDescription::decode: unknown tag: {}", + tag + ))), + }, + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "TagMismatchDescription::decode: expected tag", + )) + } + } + } + } +} +#[derive(Debug)] +pub struct FailureDescription { + pub description: String, + /// Hex-encoded base64 representation of the Plutus context + pub plutus_context_base64: String, +} + +impl Decode<'_, NodeErrorDecoder> for FailureDescription { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![3], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => { + if tag == 1 { + let description = d.str()?.to_string(); + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let plutus_context_base64 = hex::encode(d.bytes()?); + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + Ok(FailureDescription { + description, + plutus_context_base64, + }) + } else { + Err(Error::message(format!( + "FailureDescription::decode: expected tag == 1, got {}", + tag + ))) + } + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + Err(Error::message( + "FailureDescription::decode: expected u8 tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub enum AlonzoUtxowPredFailure { + ShelleyInAlonzoUtxowPredfailure(ShelleyUtxowPredFailure), + MissingRedeemers, + MissingRequiredDatums, + NotAllowedSupplementalDatums, + PPViewHashesDontMatch, + MissingRequiredSigners(Vec>), + UnspendableUtxoNoDatumHash, + ExtraRedeemers, +} + +impl Decode<'_, NodeErrorDecoder> for AlonzoUtxowPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => { + match tag { + 0 => { + let shelley_utxow_failure = ShelleyUtxowPredFailure::decode(d, ctx)?; + Ok(AlonzoUtxowPredFailure::ShelleyInAlonzoUtxowPredfailure( + shelley_utxow_failure, + )) + } + 5 => { + // MissingRequiredSigners + let signers: Result, _> = d.array_iter()?.collect(); + let signers = signers?; + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + Ok(AlonzoUtxowPredFailure::MissingRequiredSigners(signers)) + } + //7 => { + // // ExtraRedeemers + //} + _ => Err(Error::message(format!( + "AlonzoUtxowPredFailure unhandled tag {}", + tag + ))), + } + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "AlonzoUtxoPredwFailure::decode: expected tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub enum ShelleyUtxowPredFailure { + InvalidWitnessesUTXOW, + /// Witnesses which failed in verifiedWits function + MissingVKeyWitnessesUTXOW(Vec>), + MissingScriptWitnessesUTXOW(Vec), + ScriptWitnessNotValidatingUTXOW(Vec), + UtxoFailure, + MIRInsufficientGenesisSigsUTXOW, + MissingTxBodyMetadataHash, + MissingTxMetadata, + ConflictingMetadataHash, + InvalidMetadata, + ExtraneousScriptWitnessesUTXOW(Vec), +} + +impl Decode<'_, NodeErrorDecoder> for ShelleyUtxowPredFailure { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + match expect_u8(d, ctx) { + Ok(tag) => { + match tag { + 2 => { + let missing_script_witnesses: Result, _> = d.array_iter()?.collect(); + let missing_script_witnesses = missing_script_witnesses?; + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + Ok(ShelleyUtxowPredFailure::MissingScriptWitnessesUTXOW( + missing_script_witnesses, + )) + } + 1 => { + // MissingVKeyWitnessesUTXOW + let missing_vkey_witnesses: Result, _> = d.array_iter()?.collect(); + let missing_vkey_witnesses = missing_vkey_witnesses?; + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + Ok(ShelleyUtxowPredFailure::MissingVKeyWitnessesUTXOW( + missing_vkey_witnesses, + )) + } + _ => Err(Error::message("not BabbageUtxoPredFailure")), + } + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message( + "BabbageUtxoPredFailure::decode: expected tag", + )) + } + } + } + } +} + +#[derive(Debug)] +pub struct TxInput { + pub tx_hash: TxHash, + pub index: u64, +} + +impl Decode<'_, NodeErrorDecoder> for TxInput { + fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + expect_definite_array(vec![2], d, ctx)?; + let bytes = expect_bytes(d, ctx)?; + let tx_hash = TxHash::from(bytes.as_slice()); + match d.probe().int() { + Ok(index) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.int()?; + let index = + u64::try_from(index).map_err(|_| Error::message("Can't convert Int to u64"))?; + Ok(TxInput { tx_hash, index }) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message("TxInput::decode: expected index (int)")) + } + } + } + } +} + +/// Process the next CBOR token, adjusting the position if the outer scope is a definite array. +/// If this token represents a new collection, add new scope to the stack. +fn add_collection_token_to_context( + d: &mut Decoder, + ctx: &mut NodeErrorDecoder, +) -> Result<(), Error> { + let t = next_token(d)?; + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + match t { + Token::BeginArray | Token::BeginBytes | Token::BeginMap => { + ctx.context_stack.push(OuterScope::Indefinite); + } + Token::Array(n) | Token::Map(n) => { + ctx.context_stack.push(OuterScope::Definite(n)); + } + + Token::Break => { + ctx.cbor_break_token_seen = true; + } + + // Throw away the token (even break) + _ => (), + } + + Ok(()) +} + +fn expect_indefinite_array(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result<(), Error> { + match d.probe().array() { + Ok(None) => { + if let Some(OuterScope::Definite(inner_n)) = ctx.context_stack.pop() { + if inner_n > 1 { + ctx.context_stack.push(OuterScope::Definite(inner_n - 1)); + } + } + let _ = d.array()?; + Ok(()) + } + Ok(Some(n)) => { + if let Some(OuterScope::Definite(inner_n)) = ctx.context_stack.pop() { + if inner_n > 1 { + ctx.context_stack.push(OuterScope::Definite(inner_n - 1)); + } + } + ctx.context_stack.push(OuterScope::Definite(n)); + Err(Error::message(format!( + "Expected indefinite array, got array({})", + n + ))) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!( + "Expected indefinite array, error: {:?}", + e + ))) + } + } + } +} + +fn expect_bytes(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result, Error> { + match d.probe().bytes() { + Ok(bytes) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.bytes()?; + Ok(bytes.to_vec()) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message("TxInput::decode: expected bytes")) + } + } + } +} + +fn expect_int(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + match d.probe().int() { + Ok(i) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.int()?; + Ok(i) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message("expected int")) + } + } + } +} + +fn expect_definite_array( + possible_lengths: Vec, + d: &mut Decoder, + ctx: &mut NodeErrorDecoder, +) -> Result { + match d.probe().array() { + Ok(Some(len)) => { + if let Some(OuterScope::Definite(inner_n)) = ctx.context_stack.pop() { + if inner_n > 1 { + ctx.context_stack.push(OuterScope::Definite(inner_n - 1)); + } + } + ctx.context_stack.push(OuterScope::Definite(len)); + let _ = d.array()?; + if possible_lengths.is_empty() || possible_lengths.contains(&len) { + Ok(len) + } else { + Err(Error::message(format!( + "Expected array({:?}), got array({})", + possible_lengths, len + ))) + } + } + Ok(None) => { + let t = next_token(d)?; + assert!(matches!(t, Token::BeginArray)); + Err(Error::message(format!( + "Expected array({:?}), got indefinite array", + possible_lengths, + ))) + } + Err(e) => { + if e.is_end_of_input() { + // Must explicitly return this error, to allow decoding to stop early. + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!( + "Expected array({:?})", + possible_lengths, + ))) + } + } + } +} + +fn expect_u8(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + match d.probe().u8() { + Ok(value) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.u8()?; + Ok(value) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!("Expected u8: error: {:?}", e))) + } + } + } +} + +fn expect_u64(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + match d.probe().int() { + Ok(value) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.int()?; + Ok(u64::try_from(value).map_err(|e| Error::message(e.to_string()))?) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!("Expected u64, error: {:?}", e))) + } + } + } +} + +fn expect_bool(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { + match d.probe().bool() { + Ok(value) => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + let _ = d.bool()?; + Ok(value) + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!("Expected bool, error: {:?}", e))) + } + } + } +} + +fn decode_conway_value( + d: &mut Decoder, + ctx: &mut NodeErrorDecoder, +) -> Result { + use pallas_primitives::conway::Value; + match d.datatype() { + Ok(dt) => { + match dt { + minicbor::data::Type::U8 + | minicbor::data::Type::U16 + | minicbor::data::Type::U32 + | minicbor::data::Type::U64 => { + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + Ok(Value::Coin(d.decode_with(ctx)?)) + } + minicbor::data::Type::Array => { + expect_definite_array(vec![2], d, ctx)?; + let coin = expect_u64(d, ctx)?; + let multiasset = d.decode_with(ctx)?; + // If multiasset is successfully decoded, let's manually update outer scope. + if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { + if n > 1 { + ctx.context_stack.push(OuterScope::Definite(n - 1)); + } + } + + Ok(pallas_primitives::conway::Value::Multiasset( + coin, multiasset, + )) + } + _ => Err(minicbor::decode::Error::message( + "unknown cbor data type for Alonzo Value enum", + )), + } + } + Err(e) => { + if e.is_end_of_input() { + Err(e) + } else { + add_collection_token_to_context(d, ctx)?; + Err(Error::message(format!( + "Can't decode Conway Value, error: {:?}", + e + ))) + } + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OuterScope { + /// We are within a definite CBOR collection such as an array or map. The inner `u64` indicates + /// the number of elements left to be processed within the collection. + Definite(u64), + /// We are within an indefinite collection. + Indefinite, +} + +fn clear_unknown_entity(decoder: &mut Decoder, stack: &mut Vec) -> Result<(), Error> { + println!("Clear stack: {:?}", stack); + while let Some(e) = stack.pop() { + let t = next_token(decoder)?; + println!("Next token: {:?}", t); + + match e { + OuterScope::Definite(num_left) => { + if num_left > 1 { + stack.push(OuterScope::Definite(num_left - 1)); + } + } + OuterScope::Indefinite => stack.push(OuterScope::Indefinite), + } + + match t { + Token::BeginArray | Token::BeginBytes | Token::BeginMap => { + stack.push(OuterScope::Indefinite); + } + Token::Array(n) | Token::Map(n) => { + stack.push(OuterScope::Definite(n)); + } + + Token::Break => { + assert_eq!(e, OuterScope::Indefinite); + assert_eq!(stack.pop(), Some(OuterScope::Indefinite)); + } + + // Throw away the token + _ => (), + } + } + Ok(()) +} + +fn next_token<'a>(decoder: &'a mut Decoder) -> Result, Error> { + match decoder.datatype()? { + Type::Bool => decoder.bool().map(Token::Bool), + Type::U8 => decoder.u8().map(Token::U8), + Type::U16 => decoder.u16().map(Token::U16), + Type::U32 => decoder.u32().map(Token::U32), + Type::U64 => decoder.u64().map(Token::U64), + Type::I8 => decoder.i8().map(Token::I8), + Type::I16 => decoder.i16().map(Token::I16), + Type::I32 => decoder.i32().map(Token::I32), + Type::I64 => decoder.i64().map(Token::I64), + Type::Int => decoder.int().map(Token::Int), + Type::F16 => decoder.f16().map(Token::F16), + Type::F32 => decoder.f32().map(Token::F32), + Type::F64 => decoder.f64().map(Token::F64), + Type::Bytes => decoder.bytes().map(Token::Bytes), + Type::String => decoder.str().map(Token::String), + Type::Tag => decoder.tag().map(Token::Tag), + Type::Simple => decoder.simple().map(Token::Simple), + Type::Array => { + let p = decoder.position(); + if let Some(n) = decoder.array()? { + Ok(Token::Array(n)) + } else { + Err(Error::type_mismatch(Type::Array) + .at(p) + .with_message("missing array length")) + } + } + Type::Map => { + let p = decoder.position(); + if let Some(n) = decoder.map()? { + Ok(Token::Map(n)) + } else { + Err(Error::type_mismatch(Type::Array) + .at(p) + .with_message("missing map length")) + } + } + Type::BytesIndef => { + decoder.set_position(decoder.position() + 1); + Ok(Token::BeginBytes) + } + Type::StringIndef => { + decoder.set_position(decoder.position() + 1); + Ok(Token::BeginString) + } + Type::ArrayIndef => { + decoder.set_position(decoder.position() + 1); + Ok(Token::BeginArray) + } + Type::MapIndef => { + decoder.set_position(decoder.position() + 1); + Ok(Token::BeginMap) + } + Type::Null => { + decoder.set_position(decoder.position() + 1); + Ok(Token::Null) + } + Type::Undefined => { + decoder.set_position(decoder.position() + 1); + Ok(Token::Undefined) + } + Type::Break => { + decoder.set_position(decoder.position() + 1); + Ok(Token::Break) + } + t @ Type::Unknown(_) => Err(Error::type_mismatch(t) + .at(decoder.position()) + .with_message("unknown cbor type")), + } +} + +#[cfg(test)] +mod tests { + use std::{iter::repeat, path::PathBuf}; + + use itertools::Itertools; + use pallas_codec::minicbor::{ + encode::{write::EndOfSlice, Error}, + Encoder, + }; + + use crate::miniprotocols::localtxsubmission::{ + cardano_node_errors::NodeErrorDecoder, + codec::{DecodeCBORSplitPayload, DecodingResult}, + }; + + #[test] + fn test_decode_malformed_error() { + let buffer = encode_trace().unwrap(); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&buffer); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert_eq!(errors[0].non_script_errors.len(), 0); + } else { + panic!("") + } + } + + const SPLASH_DAO_EXAMPLE: &str = "82028182059f820082018200820281581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18ef82008201820783000001000300820082018200820181581c28c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382382008201820581581c0bbd6545f014f95a65b9df462088c6600d9b2bb6cee3fe20b53241ea820082028201820782018182038201825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e88018200820282018305821a002dc6c0a2581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18efa15820378d0caaaa3855f1b38693c1d6ef004fd118691c95c959d4efa950d6d6fcf7c101821a00765cada1581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000820082028201820081825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e880182018201a1581de028c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382300ff"; + const SPLASH_BOT_EXAMPLE: &str = "82028182059f820082018207830000000100028200820282018207820181820382018258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f500820082028201830500821a06760d80a1581cfd10da3e6a578708c877e14b6aaeda8dc3a36f666a346eec52a30b3aa14974657374746f6b656e1a0001fbd08200820282018200838258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f5008258205f85cf7db4713466bc8d9d32a84b5b6bfd2f34a76b5f8cf5a5cb04b4d6d6f0380082582096eb39b8d909373c8275c611fae63792f5e3d0a67c1eee5b3afb91fdcddc859100ff"; + + fn encode_trace() -> Result, Error> { + let mut buffer = repeat(0).take(24).collect_vec(); + let mut encoder = Encoder::new(&mut buffer[..]); + + let _e = encoder + .array(2)? + .u8(2)? + .array(1)? + .array(2)? + .u8(5)? + .begin_array()? + // Encode ledger errors + .array(2)? + .u8(0)? // Tag for BabbageUtxowPredFailure + .array(2)? + .u8(2)? // Tag for BabbageUtxoPredFailure + .array(2)? + .u8(1)? // Tag for AlonzoUtxoPredFailure + .array(2)? + .u8(100)? // Unsupported Tag + .array(1)? // dummy value + .array(1)? // dummy value + .array(1)? // dummy value + .array(1)? // dummy value + .array(1)? // dummy value + .array(1)? // dummy value + .u8(200)? + .end()?; + + Ok(buffer) + } + + #[test] + fn test_decode_splash_bot_example() { + let bytes = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[test] + fn test_decode_splash_dao_example() { + let bytes = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[derive(Debug, PartialEq, Eq)] + struct ScriptError { + error_description: String, + plutus_context_bytes: Vec, + } + + #[test] + fn complete_script_err() { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("test_resources/complete_script_error.txt"); + let bytes = hex::decode( + std::fs::read_to_string(path).expect("Cannot load script_error_traces.txt"), + ) + .unwrap(); + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[test] + fn split_script_err() { + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("test_resources/complete_script_error.txt"); + let mut bytes = hex::decode( + std::fs::read_to_string(path).expect("Cannot load script_error_traces.txt"), + ) + .unwrap(); + let tail = bytes.split_off(bytes.len() / 2); + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 0); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&tail); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[test] + fn combined_splash_errors() { + let mut bytes = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + bytes.extend_from_slice(&hex::decode(SPLASH_DAO_EXAMPLE).unwrap()); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + println!("{:?}", result); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 2); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[test] + fn neat_split_combined_splash_errors() { + // We have 2 node errors side-by-side, where each error's bytes are cut in half + // for partial processing. + let mut bot_bytes_0 = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + let bot_bytes_1 = bot_bytes_0.split_off(bot_bytes_0.len() / 2); + let mut dao_bytes_0 = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + let dao_bytes_1 = dao_bytes_0.split_off(dao_bytes_0.len() / 2); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bot_bytes_0); + println!("{:?}", result); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 0); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&bot_bytes_1); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&dao_bytes_0); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&dao_bytes_1); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 2); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } + + #[test] + fn mixed_split_combined_splash_errors() { + // We have 2 node errors side-by-side, where each error's bytes are cut in half + // but this is followed by cutting off a part of the end of the first error and + // prepending it to the 2nd error. + let mut bot_bytes_0 = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + let mut bot_bytes_1 = bot_bytes_0.split_off(bot_bytes_0.len() / 2); + let mut bot_bytes_2 = bot_bytes_1.split_off(bot_bytes_1.len() / 4); + let mut dao_bytes_0 = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + let dao_bytes_1 = dao_bytes_0.split_off(dao_bytes_0.len() / 2); + bot_bytes_2.extend(dao_bytes_0); + + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bot_bytes_0); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 0); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&bot_bytes_1); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 0); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&bot_bytes_2); + if let Ok(DecodingResult::Incomplete(errors)) = result { + assert_eq!(errors.len(), 1); + assert!(cc.has_undecoded_bytes()); + } else { + panic!(""); + } + + let result = cc.try_decode_with_new_bytes(&dao_bytes_1); + if let Ok(DecodingResult::Complete(errors)) = result { + assert_eq!(errors.len(), 2); + assert!(!cc.has_undecoded_bytes()); + } else { + panic!(""); + } + } +} diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index ebb9fc30..3ecdf249 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -3,41 +3,56 @@ use std::marker::PhantomData; use thiserror::Error; use tracing::debug; -use pallas_codec::Fragment; +use pallas_codec::minicbor; +use tracing::error; +use tracing::trace; use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason, State}; use crate::multiplexer; +use crate::multiplexer::AgentChannel; +use crate::multiplexer::MAX_SEGMENT_PAYLOAD_LENGTH; + +use super::cardano_node_errors::TxApplyErrors; +use super::codec::DecodeCBORSplitPayload; +use super::codec::DecodingResult; /// Cardano specific instantiation of LocalTxSubmission client. -pub type Client = GenericClient; +pub type Client<'a, ErrDecoder> = + GenericClient<'a, EraTx, DecodingResult, ErrDecoder>; /// A generic Ouroboros client for submitting a generic transaction /// to a server, which possibly results in a generic rejection. -pub struct GenericClient { +pub struct GenericClient<'a, Tx, Reject, ErrDecoder> { state: State, - muxer: multiplexer::ChannelBuffer, + muxer: LocalTxChannelBuffer<'a, Tx, Reject, ErrDecoder>, pd_tx: PhantomData, pd_reject: PhantomData, + pd_err_decoder: PhantomData, } -impl GenericClient -where - Message: Fragment, -{ +impl<'a, Tx, Reject, ErrDecoder> GenericClient<'a, Tx, Reject, ErrDecoder> { /// Constructs a new LocalTxSubmission `Client` instance. /// /// # Arguments /// * `channel` - An instance of `multiplexer::AgentChannel` to be used for /// communication. - pub fn new(channel: multiplexer::AgentChannel) -> Self { + pub fn new(channel: multiplexer::AgentChannel, err_decoder: ErrDecoder) -> Self { Self { state: State::Idle, - muxer: multiplexer::ChannelBuffer::new(channel), + muxer: LocalTxChannelBuffer::new(channel, err_decoder), pd_tx: Default::default(), pd_reject: Default::default(), + pd_err_decoder: Default::default(), } } +} +impl<'a, Tx, Reject, ErrDecoder> GenericClient<'a, Tx, Reject, ErrDecoder> +where + DecodingResult>: minicbor::Encode<()> + minicbor::Decode<'a, ErrDecoder>, + ErrDecoder: DecodeCBORSplitPayload>, + Reject: minicbor::Decode<'a, ErrDecoder> + Send + Sync + 'static, +{ /// Submits the given `tx` to the server. /// /// # Arguments @@ -57,9 +72,9 @@ where /// Returns an error if the agency is not ours or if the outbound state is /// invalid. pub async fn terminate_gracefully(&mut self) -> Result<(), Error> { - let msg = Message::Done; - self.send_message(&msg).await?; self.state = State::Done; + let msg = DecodingResult::Complete(Message::Done); + self.send_message(&msg).await?; Ok(()) } @@ -93,16 +108,27 @@ where } } - fn assert_outbound_state(&self, msg: &Message) -> Result<(), Error> { + fn assert_outbound_state( + &self, + msg: &DecodingResult>, + ) -> Result<(), Error> { match (&self.state, msg) { - (State::Idle, Message::SubmitTx(_) | Message::Done) => Ok(()), + ( + State::Idle, + DecodingResult::Complete(Message::SubmitTx(_)) + | DecodingResult::Complete(Message::Done), + ) => Ok(()), _ => Err(Error::InvalidOutbound), } } - fn assert_inbound_state(&self, msg: &Message) -> Result<(), Error> { + fn assert_inbound_state(&self, msg: &DecodingResult>) -> Result<(), Error> { match (&self.state, msg) { - (State::Busy, Message::AcceptTx | Message::RejectTx(_)) => Ok(()), + ( + State::Busy, + DecodingResult::Complete(Message::AcceptTx) + | DecodingResult::Complete(Message::RejectTx(_)), + ) => Ok(()), _ => Err(Error::InvalidInbound), } } @@ -116,7 +142,10 @@ where /// # Errors /// Returns an error if the agency is not ours or if the outbound state is /// invalid. - async fn send_message(&mut self, msg: &Message) -> Result<(), Error> { + async fn send_message( + &mut self, + msg: &DecodingResult>, + ) -> Result<(), Error> { self.assert_agency_is_ours()?; self.assert_outbound_state(msg)?; @@ -133,18 +162,24 @@ where /// # Errors /// Returns an error if the agency is not theirs or if the inbound state is /// invalid. - async fn recv_message(&mut self) -> Result, Error> { + async fn recv_message(&mut self) -> Result>, Error> { self.assert_agency_is_theirs()?; - let msg = self - .muxer - .recv_full_msg() - .await - .map_err(Error::ChannelError)?; - + let msg = { + self.muxer + .recv_full_msg() + .await + .map(DecodingResult::Complete)? + }; self.assert_inbound_state(&msg)?; - - Ok(msg) + match (&self.state, &msg) { + ( + State::Busy, + DecodingResult::Complete(Message::AcceptTx) + | DecodingResult::Complete(Message::RejectTx(_)), + ) => Ok(msg), + _ => Err(Error::InvalidInbound), + } } /// Sends SubmitTx message to the server. @@ -156,9 +191,9 @@ where /// Returns an error if the agency is not ours or if the outbound state is /// invalid. async fn send_submit_tx(&mut self, tx: Tx) -> Result<(), Error> { - let msg = Message::SubmitTx(tx); - self.send_message(&msg).await?; self.state = State::Busy; + let msg = DecodingResult::Complete(Message::SubmitTx(tx)); + self.send_message(&msg).await?; debug!("sent SubmitTx"); @@ -172,17 +207,24 @@ where async fn recv_submit_tx_response(&mut self) -> Result, Error> { debug!("waiting for SubmitTx response"); - match self.recv_message().await? { - Message::AcceptTx => { - self.state = State::Idle; + let mut set_idle = false; + let response = match self.recv_message().await? { + DecodingResult::Complete(Message::AcceptTx) => { + set_idle = true; Ok(Response::Accepted) } - Message::RejectTx(rejection) => { - self.state = State::Idle; + DecodingResult::Complete(Message::RejectTx(rejection)) => { + set_idle = true; Ok(Response::Rejected(rejection)) } _ => Err(Error::InvalidInbound), + }; + + if set_idle { + self.state = State::Idle; } + + response } } @@ -209,3 +251,76 @@ pub enum Response { Accepted, Rejected(Reject), } + +/// A channel abstraction to hide the complexity of partial payloads +struct LocalTxChannelBuffer<'a, Tx, Reject, ErrDecoder> { + channel: AgentChannel, + err_decoder: ErrDecoder, + pd_tx: PhantomData, + pd_reject: PhantomData, + pd_lifetime: PhantomData<&'a ()>, +} +impl<'a, Tx, Reject, ErrDecoder> LocalTxChannelBuffer<'a, Tx, Reject, ErrDecoder> { + pub fn new(channel: AgentChannel, err_decoder: ErrDecoder) -> Self { + Self { + channel, + err_decoder, + pd_lifetime: Default::default(), + pd_tx: Default::default(), + pd_reject: Default::default(), + } + } +} + +impl<'a, Tx, Reject, ErrDecoder> LocalTxChannelBuffer<'a, Tx, Reject, ErrDecoder> +where + DecodingResult>: minicbor::Encode<()> + minicbor::Decode<'a, ErrDecoder>, + ErrDecoder: DecodeCBORSplitPayload>, + Reject: minicbor::Decode<'a, ErrDecoder> + Send + Sync + 'static, +{ + /// Enqueues a msg as a sequence payload chunks + pub async fn send_msg_chunks(&mut self, msg: &M) -> Result<(), crate::multiplexer::Error> + where + M: minicbor::Encode<()> + minicbor::Decode<'a, ErrDecoder>, + { + let mut payload = Vec::new(); + minicbor::encode(msg, &mut payload) + .map_err(|err| crate::multiplexer::Error::Encoding(err.to_string()))?; + + let chunks = payload.chunks(MAX_SEGMENT_PAYLOAD_LENGTH); + + for chunk in chunks { + self.channel.enqueue_chunk(Vec::from(chunk)).await?; + } + + Ok(()) + } + + /// Reads from the channel until a complete message is found + pub async fn recv_full_msg(&mut self) -> Result, Error> { + loop { + let chunk: Vec = self + .channel + .dequeue_chunk() + .await + .map_err(Error::ChannelError)?; + let result = self.err_decoder.try_decode_with_new_bytes(&chunk); + + match result { + Ok(decoding_result) => match decoding_result { + DecodingResult::Complete(c) => { + return Ok(c); + } + DecodingResult::Incomplete(_) => (), + }, + Err(_e) => { + return Err(Error::InvalidInbound); + } + } + } + } + + pub fn unwrap(self) -> AgentChannel { + self.channel + } +} diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index ffd2c151..74569a15 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -36,39 +36,75 @@ where } } -impl<'b, Tx: Decode<'b, ()>, Reject: Decode<'b, ()>> Decode<'b, ()> for Message { - fn decode(d: &mut Decoder<'b>, _ctx: &mut ()) -> Result { - if d.array().is_err() { - // if the first element isn't an array, it's a plutus error - // the node sends string data - let rejection = d.decode()?; - - // skip this data via setting the decoder position, because it doesn't recognize - // it with rejection decode - d.set_position(d.input().len()); - - return Ok(Message::RejectTx(rejection)); - } +#[derive(Debug)] +pub enum DecodingResult { + Complete(Reject), + Incomplete(Reject), +} - let label = d.u16()?; +/// An implementor of this trait is able to decode an entity from CBOR with bytes that are split +/// over multiple payloads. +pub trait DecodeCBORSplitPayload { + /// Type of entity to decode + type Entity; + /// Attempt to decode entity given a new slice of bytes. + fn try_decode_with_new_bytes( + &mut self, + bytes: &[u8], + ) -> Result, decode::Error>; + /// Returns true if there still remain CBOR bytes to be decoded. + fn has_undecoded_bytes(&self) -> bool; +} - match label { - 0 => { - let tx = d.decode()?; - Ok(Message::SubmitTx(tx)) +impl<'b, C, Tx: Decode<'b, ()>, Reject> Decode<'b, C> for DecodingResult> +where + C: DecodeCBORSplitPayload, + Reject: Send + Sync + 'static, +{ + fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result { + if ctx.has_undecoded_bytes() { + let s = ctx.try_decode_with_new_bytes(d.input()); + match s { + Ok(DecodingResult::Complete(reasons)) => { + Ok(DecodingResult::Complete(Message::RejectTx(reasons))) + } + Ok(DecodingResult::Incomplete(reasons)) => { + Ok(DecodingResult::Incomplete(Message::RejectTx(reasons))) + } + Err(e) => Err(e), } - 1 => Ok(Message::AcceptTx), - 2 => { - let rejection = d.decode()?; - - // skip this data via setting the decoder position, because it doesn't recognize - // it with rejection decode - d.set_position(d.input().len()); - - Ok(Message::RejectTx(rejection)) + } else { + let mut probe = d.probe(); + if probe.array().is_err() { + // If we don't have any unprocessed bytes the first element should be an array + return Err(decode::Error::message( + "Expecting an array (no unprocessed bytes)", + )); + } + let label = probe.u16()?; + match label { + 0 => { + d.array()?; + d.u16()?; + let tx = d.decode()?; + Ok(DecodingResult::Complete(Message::SubmitTx(tx))) + } + 1 => Ok(DecodingResult::Complete(Message::AcceptTx)), + 2 => { + let s = ctx.try_decode_with_new_bytes(d.input()); + match s { + Ok(DecodingResult::Complete(reasons)) => { + Ok(DecodingResult::Complete(Message::RejectTx(reasons))) + } + Ok(DecodingResult::Incomplete(reasons)) => { + Ok(DecodingResult::Incomplete(Message::RejectTx(reasons))) + } + Err(e) => Err(e), + } + } + 3 => Ok(DecodingResult::Complete(Message::Done)), + _ => Err(decode::Error::message("can't decode Message")), } - 3 => Ok(Message::Done), - _ => Err(decode::Error::message("can't decode Message")), } } } @@ -121,16 +157,44 @@ impl Encode<()> for RejectReason { #[cfg(test)] mod tests { + use pallas_codec::minicbor::{decode, Decode}; use pallas_codec::{minicbor, Fragment}; use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason}; use crate::multiplexer::Error; + use super::{DecodeCBORSplitPayload, DecodingResult}; + + struct CBORDecoder; + + impl DecodeCBORSplitPayload for CBORDecoder { + type Entity = RejectReason; + + fn try_decode_with_new_bytes( + &mut self, + bytes: &[u8], + ) -> Result, decode::Error> { + let mut decoder = minicbor::Decoder::new(bytes); + let reason = RejectReason::decode(&mut decoder, &mut ()); + match reason { + Ok(reason) => Ok(DecodingResult::Complete(reason)), + Err(e) => { + unreachable!() + } + } + } + + fn has_undecoded_bytes(&self) -> bool { + false + } + } + #[test] fn decode_reject_message() { - let mut bytes = hex::decode(RAW_REJECT_RESPONSE).unwrap(); - let msg_res = try_decode_message::>(&mut bytes); - assert!(msg_res.is_ok()) + let bytes = hex::decode(RAW_REJECT_RESPONSE).unwrap(); + let mut decoder = minicbor::Decoder::new(&bytes); + let _maybe_msg: DecodingResult> = + decoder.decode_with(&mut CBORDecoder).unwrap(); } fn try_decode_message(buffer: &mut Vec) -> Result, Error> @@ -153,10 +217,11 @@ mod tests { #[test] fn decode_reject_string_message() { - let mut bytes = hex::decode(RAW_REJECT_REPONSE_ERROR_STRING).unwrap(); - let msg_res = try_decode_message::>(&mut bytes); - println!("result {:?}", msg_res); - assert!(msg_res.is_ok()) + // let mut bytes = hex::decode(RAW_REJECT_REPONSE_ERROR_STRING).unwrap(); + // let msg_res = + // try_decode_message::>>(&mut bytes); + // println!("result {:?}", msg_res); + // assert!(msg_res.is_ok()) } const RAW_REJECT_RESPONSE: &str = diff --git a/pallas-network/src/miniprotocols/localtxsubmission/mod.rs b/pallas-network/src/miniprotocols/localtxsubmission/mod.rs index 73e980aa..3474660a 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/mod.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/mod.rs @@ -1,6 +1,7 @@ pub use client::*; pub use protocol::*; +pub mod cardano_node_errors; mod client; mod codec; mod protocol; diff --git a/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs b/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs index 3a2cc4db..537ff267 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs @@ -17,6 +17,7 @@ pub enum Message { #[derive(Debug, Clone, Eq, PartialEq)] pub struct EraTx(pub u16, pub Vec); -// Raw reject reason. +/// Raw reject reason, as CBOR bytes. Note that the given bytes may not represent a complete error +/// response, as the multiplexer's segment length is at most `MAX_SEGMENT_PAYLOAD_LENGTH` bytes. #[derive(Debug, Clone, Eq, PartialEq)] pub struct RejectReason(pub Vec); diff --git a/pallas-network/test_resources/complete_script_error.txt b/pallas-network/test_resources/complete_script_error.txt new file mode 100644 index 00000000..6ed918db --- /dev/null +++ b/pallas-network/test_resources/complete_script_error.txt @@ -0,0 +1 @@ +82028182059f82008202820182078300f58201818301792dcb0a54686520506c757475735632207363726970742028506c7574757353637269707420506c7574757356322053637269707448617368202266363066643165373066346239646663303963646465386437663766313237376465323639346338326135313664376433636339653033652229206661696c732e0a54686520706c75747573206572726f722069733a2043656b4572726f7220416e206572726f7220686173206f636375727265643a202055736572206572726f723a0a546865206d616368696e65207465726d696e617465642062656361757365206f6620616e206572726f722c206569746865722066726f6d2061206275696c742d696e2066756e6374696f6e206f722066726f6d20616e206578706c6963697420757365206f6620276572726f72272e0a5468652070726f746f636f6c2076657273696f6e2069733a2050726f74566572207b70764d616a6f72203d2056657273696f6e20382c2070764d696e6f72203d20307d0a54686520646174756d2069733a20436f6e7374722030205b436f6e7374722030205b422022465c3137395c3230345c3133334c5c3231315c3235355c313735575c3231335c3234345c3234305c313433445c3133365c3137355c3134345c3133345c3231345c323430792d71437e5c4443345c3138335c323233222c42202253504c4153485f4144415f4e4654225d2c436f6e7374722030205b422022222c422022225d2c436f6e7374722030205b4220225c3233365c3233365c3230312a5c3233345c3137305c3139335c3234355c313832655c323435675c3137365c4553435c3137345c3230305c31383827715c3132384b4c21716a5c3133355c3136345c323237222c42202253504c415348225d2c436f6e7374722030205b4220224c2f5c53495c3230365c3132385a5c6e5c3137342b3f5c3232365c4443345c3230335c3139355c3135375f5c3230385c26375c3135335c3233386d2a775c43414e5c3138355c3232335c3135355c454f54222c42202253504c4153485f4144415f4c51225d2c492039393030302c49203130302c4920323835363735333937362c492031373934373835383732322c4c697374205b436f6e7374722030205b436f6e7374722031205b4220225c455458515c5355425c3230385c3134335c3137385c3138355c3231394c5c225c4443325c31363668305c3136335c3138315c3232394a5c622a5c3137332b5c3132385b5c323131583b5c454e51225d5d5d2c422022755c313936575c534f5c313832255c3137345c3133365c455343325c3136334c525c313737595c3234365c3234335c3234335c3234325c3139395c3137305c3137315c3234355c3138365c313936685c3132395c2633225d0a5468652072656465656d65722069733a20436f6e7374722030205b4920322c4920315d0a5468652073637269707420636f6e746578742069733a0a507572706f73653a205370656e64696e67202854784f7574526566207b74784f75745265664964203d20636637626461656333643466333530653330616233636662346530356338353836326139636164373332393532646361336163396361303438323461356335622c2074784f7574526566496478203d20317d290a5478496e666f3a0a2020547849643a20633430633261326563333232393230636236303032623135333330393637643234663034656138303563616433386666613431303763626137343936353337320a2020496e707574733a205b20333663316639626234316533643638346634663239363364323965623337613338623737616566323063353634306532616338366463326561646439666266352131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c32353030303030295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c363539323939303433295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20646265376133643861316438323939303939326133386565613161326566616136386539333165323532666339326361313338333830396220285374616b696e6748617368205075624b657943726564656e7469616c3a206434313534636338303837333438396434656264356665646633313362323930373566363134646366646166386662623061636161343761290a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020696e6c696e6520646174756d203a20203c41413d3d2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202039616b677573494243684f7a725969572b32726c684a5742666d32634f76357a6534563669773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e493e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203635393239393034332c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203530303030302c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202037383235343034392c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c37383235343034392c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203635393239393034333e2c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020302c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c3c55534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d3e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c3c3c3142564d7941687a534a314f76562f7438784f796b485832464e7a3972342b374373716b65673d3d3e3e3e3e2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202055534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205b4c352f7754596b5576325457636141394e4b74354e2b7442654448716135392f764b755739513d3d5d3e0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a0a202020202020202020202c20636637626461656333643466333530653330616233636662346530356338353836326139636164373332393532646361336163396361303438323461356335622131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c31313731383938363639383431295d7d292c2834366233636338353463643366666166353764356634663038663434383861663930383664366630373932643731343337653134623764662c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4e4654222c31295d7d292c2834633266306663653830356130616165326233666532313463626333396435666430333739396565366432613737313862396466396230342c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4c51222c39323233333536303037373230353630323437295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c3331313839313032373531323831295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20663630666431653730663462396466633039636464653864376637663132373764653236393463383261353136643764336363396530336520285374616b696e67486173682053637269707443726564656e7469616c3a206232663661626636306363646539326561653161326634666466363566326561663632303864383732633666306535393763633130623037290a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020696e6c696e6520646174756d203a20203c3c5272504d68557a542f363958316654776a30534972354347317642354c5846446668533333773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e49583046455156394f526c513d3e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e493e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c544338507a6f426143713472502b495579384f64583941336d6535744b6e635975642b6242413d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e49583046455156394d55513d3d3e2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202039393030302c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203130302c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020323835363735333937362c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202031373934373835383732322c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205b3c3c4131456130492b797564744d49684b6d6144436a7465564b434371744b3442623031673742513d3d3e3e5d2c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020646352584472596c726f67624d714e4d5572465a3976507a38736571712f5736784769424d773d3d3e0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205d0a20205265666572656e636520696e707574733a205b20306136303139326134326562323661643563363661373537373738383533343263343062323434613335353835653934613765316164393364373630393431652130202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c3231393530383330295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202066363066643165373066346239646663303963646465386437663766313237376465323639346338326135313664376433636339653033650a20202020202020202020202020202020202020202c20626265323137363430663565663437663266643065666437303732346638646662663637346231333736646466666233303831376430376337653531326431652131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c39303736383630295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202032323161643834353331333833373930346331356130663031303764643063626538626466346134313730313836366461626539393665340a20202020202020202020202020202020202020202c20636230613563363739393364336266613936356632623934386639393735653033303861323563366334376530356536313737333635366663353861643133632130202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c35353334303430295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206462653761336438613164383239393039393261333865656131613265666161363865393331653235326663393263613133383338303962205d0a20204f7574707574733a205b202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c3939393635313932295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c33295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202053637269707443726564656e7469616c3a20646265376133643861316438323939303939326133386565613161326566616136386539333165323532666339326361313338333830396220285374616b696e6748617368205075624b657943726564656e7469616c3a206434313534636338303837333438396434656264356665646633313362323930373566363134646366646166386662623061636161343761290a2020202020202020202020202020207769746820646174756d0a202020202020202020202020202020696e6c696e6520646174756d203a20203c41413d3d2c0a20202020202020202020202020202039616b677573494243684f7a725969572b32726c684a5742666d32634f76357a6534563669773d3d2c0a2020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c205531424d51564e493e2c0a202020202020202020202020202020332c0a2020202020202020202020202020203530303030302c0a20202020202020202020202020202037383235343034392c0a2020202020202020202020202020203c2c203e2c0a2020202020202020202020202020203c37383235343034392c203635393239393034333e2c0a202020202020202020202020202020302c0a2020202020202020202020202020203c3c55534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d3e2c0a2020202020202020202020202020203c3c3c3142564d7941687a534a314f76562f7438784f796b485832464e7a3972342b374373716b65673d3d3e3e3e3e2c0a20202020202020202020202020202055534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d2c0a2020202020202020202020202020205b4c352f7754596b5576325457636141394e4b74354e2b7442654448716135392f764b755739513d3d5d3e0a20202020202020202020202020202077697468207265666572656e63655363726970740a0a20202020202020202020202c202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c31313731383030383537363132295d7d292c2834366233636338353463643366666166353764356634663038663434383861663930383664366630373932643731343337653134623764662c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4e4654222c31295d7d292c2834633266306663653830356130616165326233666532313463626333396435666430333739396565366432613737313862396466396230342c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4c51222c39323233333536303037373230353630323437295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c3331313839373632303530333231295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202053637269707443726564656e7469616c3a20663630666431653730663462396466633039636464653864376637663132373764653236393463383261353136643764336363396530336520285374616b696e67486173682053637269707443726564656e7469616c3a206232663661626636306363646539326561653161326634666466363566326561663632303864383732633666306535393763633130623037290a2020202020202020202020202020207769746820646174756d0a202020202020202020202020202020696e6c696e6520646174756d203a20203c3c5272504d68557a542f363958316654776a30534972354347317642354c5846446668533333773d3d2c0a2020202020202020202020202020205531424d51564e49583046455156394f526c513d3e2c0a2020202020202020202020202020203c2c203e2c0a2020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c205531424d51564e493e2c0a2020202020202020202020202020203c544338507a6f426143713472502b495579384f64583941336d6535744b6e635975642b6242413d3d2c205531424d51564e49583046455156394d55513d3d3e2c0a20202020202020202020202020202039393030302c0a2020202020202020202020202020203130302c0a202020202020202020202020202020323835363735333937362c0a20202020202020202020202020202031373934383531383031382c0a2020202020202020202020202020205b3c3c4131456130492b797564744d49684b6d6144436a7465564b434371744b3442623031673742513d3d3e3e5d2c0a202020202020202020202020202020646352584472596c726f67624d714e4d5572465a3976507a38736571712f5736784769424d773d3d3e0a20202020202020202020202020202077697468207265666572656e63655363726970740a202020202020202020202020202020205d0a20204665653a2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c333437303337295d7d295d7d7d0a202056616c7565206d696e7465643a2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c30295d7d295d7d7d0a20204443657274733a205b5d0a20205764726c3a205b2028205374616b696e67486173682053637269707443726564656e7469616c3a2032323161643834353331333833373930346331356130663031303764643063626538626466346134313730313836366461626539393665340a20202020202020202c20302029205d0a202056616c69642072616e67653a20282de2889e202c202be2889e290a20205369676e61746f726965733a205b32663966663034643839313462663634643637316130336433346162373933376562343137383331656136623966376662636162393666355d0a202052656465656d6572733a205b2028205370656e64696e67202854784f7574526566207b74784f75745265664964203d20333663316639626234316533643638346634663239363364323965623337613338623737616566323063353634306532616338366463326561646439666266352c2074784f7574526566496478203d20317d290a202020202020202020202020202c203c3e20290a202020202020202020202020202c2028205370656e64696e67202854784f7574526566207b74784f75745265664964203d20636637626461656333643466333530653330616233636662346530356338353836326139636164373332393532646361336163396361303438323461356335622c2074784f7574526566496478203d20317d290a202020202020202020202020202c203c322c20313e20290a202020202020202020202020202c202820526577617264696e6720285374616b696e6748617368202853637269707443726564656e7469616c20323231616438343533313338333739303463313561306630313037646430636265386264663461343137303138363664616265393936653429290a202020202020202020202020202c205b5d2029205d0a2020446174756d733a205b5d0a592af46867475972786f4141794e6847514d734151455a412b675a416a734141526b4436426c65635151424751506f474341614141484b64686b6f3677515a576467595a426c5a3242686b47566e594747515a576467595a426c5a3242686b47566e59474751595a42686b47566e594747515a5446455949426f4141717a364743415a74564545476741445978555a4166384141526f4141567731474341614141655864526b3239415143476741432f3551614141627165426a63414145424751506f47572f324241496141414f3943426f414130374647443442476741514c67385a4d536f42476741444c6f415a4161554247674143326e675a412b675a7a775942476741424f6a515949426d6f385267674751506f47434161414145367241455a34554d454751506f43686f414177495a474a77424767414441686b596e41456141414d6766426b423251456141414d7741426b422f77455a7a504d5949426e395142676747662f564743415a5742345949426c4173786767476741424b74385949426f4141762b5547674147366e67593341414241526f4141512b534753326e4141455a3672735949426f4141762b5547674147366e67593341414241526f4141762b5547674147366e67593341414241526f4145624973476741462f64344141686f414446424f4758635342426f414857723247674142516c73454767414544475941424141614141465071786767476741444932455a4179774241526d67336867674782078300f58201818301792dcb0a54686520506c757475735632207363726970742028506c7574757353637269707420506c7574757356322053637269707448617368202266363066643165373066346239646663303963646465386437663766313237376465323639346338326135313664376433636339653033652229206661696c732e0a54686520706c75747573206572726f722069733a2043656b4572726f7220416e206572726f7220686173206f636375727265643a202055736572206572726f723a0a546865206d616368696e65207465726d696e617465642062656361757365206f6620616e206572726f722c206569746865722066726f6d2061206275696c742d696e2066756e6374696f6e206f722066726f6d20616e206578706c6963697420757365206f6620276572726f72272e0a5468652070726f746f636f6c2076657273696f6e2069733a2050726f74566572207b70764d616a6f72203d2056657273696f6e20382c2070764d696e6f72203d20307d0a54686520646174756d2069733a20436f6e7374722030205b436f6e7374722030205b422022465c3137395c3230345c3133334c5c3231315c3235355c313735575c3231335c3234345c3234305c313433445c3133365c3137355c3134345c3133345c3231345c323430792d71437e5c4443345c3138335c323233222c42202253504c4153485f4144415f4e4654225d2c436f6e7374722030205b422022222c422022225d2c436f6e7374722030205b4220225c3233365c3233365c3230312a5c3233345c3137305c3139335c3234355c313832655c323435675c3137365c4553435c3137345c3230305c31383827715c3132384b4c21716a5c3133355c3136345c323237222c42202253504c415348225d2c436f6e7374722030205b4220224c2f5c53495c3230365c3132385a5c6e5c3137342b3f5c3232365c4443345c3230335c3139355c3135375f5c3230385c26375c3135335c3233386d2a775c43414e5c3138355c3232335c3135355c454f54222c42202253504c4153485f4144415f4c51225d2c492039393030302c49203130302c4920323835363735333937362c492031373934373835383732322c4c697374205b436f6e7374722030205b436f6e7374722031205b4220225c455458515c5355425c3230385c3134335c3137385c3138355c3231394c5c225c4443325c31363668305c3136335c3138315c3232394a5c622a5c3137332b5c3132385b5c323131583b5c454e51225d5d5d2c422022755c313936575c534f5c313832255c3137345c3133365c455343325c3136334c525c313737595c3234365c3234335c3234335c3234325c3139395c3137305c3137315c3234355c3138365c313936685c3132395c2633225d0a5468652072656465656d65722069733a20436f6e7374722030205b4920322c4920315d0a5468652073637269707420636f6e746578742069733a0a507572706f73653a205370656e64696e67202854784f7574526566207b74784f75745265664964203d20636637626461656333643466333530653330616233636662346530356338353836326139636164373332393532646361336163396361303438323461356335622c2074784f7574526566496478203d20317d290a5478496e666f3a0a2020547849643a20633430633261326563333232393230636236303032623135333330393637643234663034656138303563616433386666613431303763626137343936353337320a2020496e707574733a205b20333663316639626234316533643638346634663239363364323965623337613338623737616566323063353634306532616338366463326561646439666266352131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c32353030303030295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c363539323939303433295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20646265376133643861316438323939303939326133386565613161326566616136386539333165323532666339326361313338333830396220285374616b696e6748617368205075624b657943726564656e7469616c3a206434313534636338303837333438396434656264356665646633313362323930373566363134646366646166386662623061636161343761290a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020696e6c696e6520646174756d203a20203c41413d3d2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202039616b677573494243684f7a725969572b32726c684a5742666d32634f76357a6534563669773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e493e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203635393239393034332c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203530303030302c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202037383235343034392c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c37383235343034392c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203635393239393034333e2c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020302c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c3c55534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d3e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c3c3c3142564d7941687a534a314f76562f7438784f796b485832464e7a3972342b374373716b65673d3d3e3e3e3e2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202055534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205b4c352f7754596b5576325457636141394e4b74354e2b7442654448716135392f764b755739513d3d5d3e0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a0a202020202020202020202c20636637626461656333643466333530653330616233636662346530356338353836326139636164373332393532646361336163396361303438323461356335622131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c31313731383938363639383431295d7d292c2834366233636338353463643366666166353764356634663038663434383861663930383664366630373932643731343337653134623764662c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4e4654222c31295d7d292c2834633266306663653830356130616165326233666532313463626333396435666430333739396565366432613737313862396466396230342c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4c51222c39323233333536303037373230353630323437295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c3331313839313032373531323831295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20663630666431653730663462396466633039636464653864376637663132373764653236393463383261353136643764336363396530336520285374616b696e67486173682053637269707443726564656e7469616c3a206232663661626636306363646539326561653161326634666466363566326561663632303864383732633666306535393763633130623037290a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020696e6c696e6520646174756d203a20203c3c5272504d68557a542f363958316654776a30534972354347317642354c5846446668533333773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e49583046455156394f526c513d3e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e493e2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203c544338507a6f426143713472502b495579384f64583941336d6535744b6e635975642b6242413d3d2c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205531424d51564e49583046455156394d55513d3d3e2c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202039393030302c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020203130302c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020323835363735333937362c0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202031373934373835383732322c0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205b3c3c4131456130492b797564744d49684b6d6144436a7465564b434371744b3442623031673742513d3d3e3e5d2c0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020646352584472596c726f67624d714e4d5572465a3976507a38736571712f5736784769424d773d3d3e0a20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020205d0a20205265666572656e636520696e707574733a205b20306136303139326134326562323661643563363661373537373738383533343263343062323434613335353835653934613765316164393364373630393431652130202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c3231393530383330295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202066363066643165373066346239646663303963646465386437663766313237376465323639346338326135313664376433636339653033650a20202020202020202020202020202020202020202c20626265323137363430663565663437663266643065666437303732346638646662663637346231333736646466666233303831376430376337653531326431652131202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c39303736383630295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202032323161643834353331333833373930346331356130663031303764643063626538626466346134313730313836366461626539393665340a20202020202020202020202020202020202020202c20636230613563363739393364336266613936356632623934386639393735653033303861323563366334376530356536313737333635366663353861643133632130202d3e202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c35353334303430295d7d295d7d7d2061646472657373656420746f0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202053637269707443726564656e7469616c3a20346563353066323632346261363230343362656534343434386263396233383866613137326530623733656639346333616232663830393420286e6f207374616b696e672063726564656e7469616c290a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207769746820646174756d0a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206e6f20646174756d0a2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202077697468207265666572656e63655363726970740a202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020206462653761336438613164383239393039393261333865656131613265666161363865393331653235326663393263613133383338303962205d0a20204f7574707574733a205b202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c3939393635313932295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c33295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202053637269707443726564656e7469616c3a20646265376133643861316438323939303939326133386565613161326566616136386539333165323532666339326361313338333830396220285374616b696e6748617368205075624b657943726564656e7469616c3a206434313534636338303837333438396434656264356665646633313362323930373566363134646366646166386662623061636161343761290a2020202020202020202020202020207769746820646174756d0a202020202020202020202020202020696e6c696e6520646174756d203a20203c41413d3d2c0a20202020202020202020202020202039616b677573494243684f7a725969572b32726c684a5742666d32634f76357a6534563669773d3d2c0a2020202020202020202020202020203c374f7a4a4b757171776657325a66566e73427575794c776e6359424c54434678616f656b34773d3d2c205531424d51564e493e2c0a202020202020202020202020202020332c0a2020202020202020202020202020203530303030302c0a20202020202020202020202020202037383235343034392c0a2020202020202020202020202020203c2c203e2c0a2020202020202020202020202020203c37383235343034392c203635393239393034333e2c0a202020202020202020202020202020302c0a2020202020202020202020202020203c3c55534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d3e2c0a2020202020202020202020202020203c3c3c3142564d7941687a534a314f76562f7438784f796b485832464e7a3972342b374373716b65673d3d3e3e3e3e2c0a20202020202020202020202020202055534a5071647368526f5a7274673337456945694d6a65786d77772f327261494144717854513d3d2c0a2020202020202020202020202020205b4c352f7754596b5576325457636141394e4b74354e2b7442654448716135392f764b755739513d3d5d3e0a20202020202020202020202020202077697468207265666572656e63655363726970740a0a20202020202020202020202c202d2056616c7565207b67657456616c7565203d204d6170207b756e4d6170203d205b282c4d6170207b756e4d6170203d205b2822222c31313731383030383537363132295d7d292c2834366233636338353463643366666166353764356634663038663434383861663930383664366630373932643731343337653134623764662c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4e4654222c31295d7d292c2834633266306663653830356130616165326233666532313463626333396435666430333739396565366432613737313862396466396230342c4d6170207b756e4d6170203d205b282253504c4153485f4144415f4c51222c39323233333536303037373230353630323437295d7d292c2865636563633932616561616163316635623636356635363762303162616563386263323737313830346234633231373136613837613465332c4d6170207b756e4d6170203d205b282253504c415348222c3331313839373632303530333231295d7d295d7d7d2061646472657373656420746f0a20202020202020202020202020202053637269707443726564656e7469616c3a20663630666431653730663462396466633039636464653864376637663132373764653236393463383261353136643764336363396530336520285374616b696e67486173682053637269707443726564656e7469616c3a206232663661626636306363646539326561653161326634666466363566326561663632303864383732633666306535393763633130623037290a2020202020202020202020202020207769746820646174756d0a202020202020202020202020202020696e6c696e6520646174756d203a20203c3c5272504d68557a542f363958316654776a30534972354347317642354c5846446668533333773d3d2c0a2020202020202020202020202020205531424d51564e49583046455156394f526c513d3eff \ No newline at end of file diff --git a/pallas-network/tests/protocols.rs b/pallas-network/tests/protocols.rs index d67c2d6a..e7fbc496 100644 --- a/pallas-network/tests/protocols.rs +++ b/pallas-network/tests/protocols.rs @@ -14,6 +14,7 @@ use pallas_network::miniprotocols::localstate::queries_v16::{ Value, }; use pallas_network::miniprotocols::localstate::ClientQueryRequest; +use pallas_network::miniprotocols::localtxsubmission::cardano_node_errors::NodeErrorDecoder; use pallas_network::miniprotocols::txsubmission::{EraTxBody, TxIdAndSize}; use pallas_network::miniprotocols::{ blockfetch, @@ -799,7 +800,9 @@ pub async fn local_state_query_server_and_client_happy_path() { // client setup let socket_path = "node1.socket"; - let mut client = NodeClient::connect(socket_path, 0).await.unwrap(); + let mut client = NodeClient::connect(socket_path, 0, NodeErrorDecoder::default()) + .await + .unwrap(); // client sends acquire From 1b34a81d692446fb6b9822aa48eb202834a3953d Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:15:39 +1000 Subject: [PATCH 02/15] Fix generic type in localtxsubmission Client --- pallas-network/src/miniprotocols/localtxsubmission/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index 3ecdf249..d2c9937c 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -18,7 +18,7 @@ use super::codec::DecodingResult; /// Cardano specific instantiation of LocalTxSubmission client. pub type Client<'a, ErrDecoder> = - GenericClient<'a, EraTx, DecodingResult, ErrDecoder>; + GenericClient<'a, EraTx, DecodingResult>, ErrDecoder>; /// A generic Ouroboros client for submitting a generic transaction /// to a server, which possibly results in a generic rejection. From 052cd8439213aa71eac4bd8e4477eda2eedfcd9b Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Thu, 25 Jul 2024 03:49:31 +1000 Subject: [PATCH 03/15] Impl error decoding on concrete types instead of generic --- .../localtxsubmission/cardano_node_errors.rs | 127 +++++++++++++----- .../miniprotocols/localtxsubmission/codec.rs | 56 +------- 2 files changed, 97 insertions(+), 86 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index 3241f37d..f7862d8c 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -7,9 +7,9 @@ use pallas_codec::minicbor::{ use pallas_primitives::conway::ScriptHash; use pallas_utxorpc::TxHash; -use crate::miniprotocols::localtxsubmission::codec::DecodingResult; +use crate::miniprotocols::localtxsubmission::{codec::DecodingResult, Message}; -use super::codec::DecodeCBORSplitPayload; +use super::{codec::DecodeCBORSplitPayload, EraTx}; /// Decodes Cardano node errors whose CBOR byte representation could be split over multiple /// payloads. @@ -52,36 +52,87 @@ impl Default for NodeErrorDecoder { } impl DecodeCBORSplitPayload for NodeErrorDecoder { - type Entity = Vec; + type Entity = Message>; fn try_decode_with_new_bytes( &mut self, bytes: &[u8], ) -> Result, decode::Error> { - self.response_bytes.extend_from_slice(bytes); - let bytes = self.response_bytes.clone(); - let mut decoder = Decoder::new(&bytes); - let mut errors = vec![]; - - loop { - match TxApplyErrors::decode(&mut decoder, self) { - Ok(tx_err) => { - errors.push(tx_err); - } - Err(e) => { - if !e.is_end_of_input() { - return Err(e); - } else { - break; + if self.has_undecoded_bytes() { + self.response_bytes.extend_from_slice(bytes); + let bytes = self.response_bytes.clone(); + let mut decoder = Decoder::new(&bytes); + let mut errors = vec![]; + + loop { + match TxApplyErrors::decode(&mut decoder, self) { + Ok(tx_err) => { + errors.push(tx_err); + } + Err(e) => { + if !e.is_end_of_input() { + return Err(e); + } else { + break; + } } } } - } - if self.has_undecoded_bytes() { - Ok(DecodingResult::Incomplete(errors)) + if self.has_undecoded_bytes() { + Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) + } else { + Ok(DecodingResult::Complete(Message::RejectTx(errors))) + } } else { - Ok(DecodingResult::Complete(errors)) + // If it's not an error response then process it right here and return. + let mut d = Decoder::new(bytes); + let mut probe = d.probe(); + if probe.array().is_err() { + // If we don't have any unprocessed bytes the first element should be an array + return Err(decode::Error::message( + "Expecting an array (no unprocessed bytes)", + )); + } + let label = probe.u16()?; + match label { + 0 => { + d.array()?; + d.u16()?; + let tx = d.decode()?; + Ok(DecodingResult::Complete(Message::SubmitTx(tx))) + } + 1 => Ok(DecodingResult::Complete(Message::AcceptTx)), + 2 => { + self.response_bytes.extend_from_slice(bytes); + let bytes = self.response_bytes.clone(); + let mut decoder = Decoder::new(&bytes); + let mut errors = vec![]; + + loop { + match TxApplyErrors::decode(&mut decoder, self) { + Ok(tx_err) => { + errors.push(tx_err); + } + Err(e) => { + if !e.is_end_of_input() { + return Err(e); + } else { + break; + } + } + } + } + + if self.has_undecoded_bytes() { + Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) + } else { + Ok(DecodingResult::Complete(Message::RejectTx(errors))) + } + } + 3 => Ok(DecodingResult::Complete(Message::Done)), + _ => Err(decode::Error::message("can't decode Message")), + } } } @@ -1073,6 +1124,7 @@ mod tests { use crate::miniprotocols::localtxsubmission::{ cardano_node_errors::NodeErrorDecoder, codec::{DecodeCBORSplitPayload, DecodingResult}, + Message, }; #[test] @@ -1081,7 +1133,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&buffer); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert_eq!(errors[0].non_script_errors.len(), 0); } else { @@ -1130,7 +1182,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { @@ -1144,7 +1196,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { @@ -1168,7 +1220,7 @@ mod tests { .unwrap(); let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { @@ -1187,7 +1239,8 @@ mod tests { let tail = bytes.split_off(bytes.len() / 2); let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); - if let Ok(DecodingResult::Incomplete(errors)) = result { + println!("{:?}", result); + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 0); assert!(cc.has_undecoded_bytes()); } else { @@ -1195,7 +1248,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&tail); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { @@ -1211,7 +1264,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); println!("{:?}", result); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 2); assert!(!cc.has_undecoded_bytes()); } else { @@ -1231,7 +1284,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bot_bytes_0); println!("{:?}", result); - if let Ok(DecodingResult::Incomplete(errors)) = result { + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 0); assert!(cc.has_undecoded_bytes()); } else { @@ -1239,7 +1292,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&bot_bytes_1); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { @@ -1247,7 +1300,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&dao_bytes_0); - if let Ok(DecodingResult::Incomplete(errors)) = result { + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(cc.has_undecoded_bytes()); } else { @@ -1255,7 +1308,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&dao_bytes_1); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 2); assert!(!cc.has_undecoded_bytes()); } else { @@ -1277,7 +1330,7 @@ mod tests { let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bot_bytes_0); - if let Ok(DecodingResult::Incomplete(errors)) = result { + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 0); assert!(cc.has_undecoded_bytes()); } else { @@ -1285,7 +1338,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&bot_bytes_1); - if let Ok(DecodingResult::Incomplete(errors)) = result { + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 0); assert!(cc.has_undecoded_bytes()); } else { @@ -1293,7 +1346,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&bot_bytes_2); - if let Ok(DecodingResult::Incomplete(errors)) = result { + if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); assert!(cc.has_undecoded_bytes()); } else { @@ -1301,7 +1354,7 @@ mod tests { } let result = cc.try_decode_with_new_bytes(&dao_bytes_1); - if let Ok(DecodingResult::Complete(errors)) = result { + if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 2); assert!(!cc.has_undecoded_bytes()); } else { diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index 74569a15..0e2aabc5 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -3,6 +3,8 @@ use pallas_codec::minicbor::{decode, encode, Decode, Decoder, Encode, Encoder}; use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason}; +use super::cardano_node_errors::TxApplyErrors; + impl Encode<()> for Message where Tx: Encode<()>, @@ -56,56 +58,12 @@ pub trait DecodeCBORSplitPayload { fn has_undecoded_bytes(&self) -> bool; } -impl<'b, C, Tx: Decode<'b, ()>, Reject> Decode<'b, C> for DecodingResult> +impl<'b, C> Decode<'b, C> for DecodingResult>> where - C: DecodeCBORSplitPayload, - Reject: Send + Sync + 'static, + C: DecodeCBORSplitPayload>>, { fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result { - if ctx.has_undecoded_bytes() { - let s = ctx.try_decode_with_new_bytes(d.input()); - match s { - Ok(DecodingResult::Complete(reasons)) => { - Ok(DecodingResult::Complete(Message::RejectTx(reasons))) - } - Ok(DecodingResult::Incomplete(reasons)) => { - Ok(DecodingResult::Incomplete(Message::RejectTx(reasons))) - } - Err(e) => Err(e), - } - } else { - let mut probe = d.probe(); - if probe.array().is_err() { - // If we don't have any unprocessed bytes the first element should be an array - return Err(decode::Error::message( - "Expecting an array (no unprocessed bytes)", - )); - } - let label = probe.u16()?; - match label { - 0 => { - d.array()?; - d.u16()?; - let tx = d.decode()?; - Ok(DecodingResult::Complete(Message::SubmitTx(tx))) - } - 1 => Ok(DecodingResult::Complete(Message::AcceptTx)), - 2 => { - let s = ctx.try_decode_with_new_bytes(d.input()); - match s { - Ok(DecodingResult::Complete(reasons)) => { - Ok(DecodingResult::Complete(Message::RejectTx(reasons))) - } - Ok(DecodingResult::Incomplete(reasons)) => { - Ok(DecodingResult::Incomplete(Message::RejectTx(reasons))) - } - Err(e) => Err(e), - } - } - 3 => Ok(DecodingResult::Complete(Message::Done)), - _ => Err(decode::Error::message("can't decode Message")), - } - } + ctx.try_decode_with_new_bytes(d.input()) } } @@ -193,8 +151,8 @@ mod tests { fn decode_reject_message() { let bytes = hex::decode(RAW_REJECT_RESPONSE).unwrap(); let mut decoder = minicbor::Decoder::new(&bytes); - let _maybe_msg: DecodingResult> = - decoder.decode_with(&mut CBORDecoder).unwrap(); + //let _maybe_msg: DecodingResult> = + //decoder.decode_with(&mut CBORDecoder).unwrap(); } fn try_decode_message(buffer: &mut Vec) -> Result, Error> From eb45b7095179efe583e8005eaf1b1b96c3e70685 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Thu, 25 Jul 2024 03:53:46 +1000 Subject: [PATCH 04/15] Change wrong generic error in localtxsubmission Client --- pallas-network/src/miniprotocols/localtxsubmission/client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index d2c9937c..2ae432dc 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -17,8 +17,7 @@ use super::codec::DecodeCBORSplitPayload; use super::codec::DecodingResult; /// Cardano specific instantiation of LocalTxSubmission client. -pub type Client<'a, ErrDecoder> = - GenericClient<'a, EraTx, DecodingResult>, ErrDecoder>; +pub type Client<'a, ErrDecoder> = GenericClient<'a, EraTx, Vec, ErrDecoder>; /// A generic Ouroboros client for submitting a generic transaction /// to a server, which possibly results in a generic rejection. From bcbf4cda1a50d6cc373628f77f2aa737f0ff5cfd Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Thu, 25 Jul 2024 16:43:01 +1000 Subject: [PATCH 05/15] Add Encoding stubs --- .../localtxsubmission/cardano_node_errors.rs | 12 +++++++++++- .../src/miniprotocols/localtxsubmission/codec.rs | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index f7862d8c..3c4c44e2 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -2,7 +2,7 @@ use pallas_codec::minicbor::{ self, data::{Int, Type}, decode::{self, Error, Token}, - Decode, Decoder, + Decode, Decoder, Encode, }; use pallas_primitives::conway::ScriptHash; use pallas_utxorpc::TxHash; @@ -146,6 +146,16 @@ pub struct TxApplyErrors { pub non_script_errors: Vec, } +impl Encode<()> for TxApplyErrors { + fn encode( + &self, + e: &mut minicbor::Encoder, + ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + todo!() + } +} + impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { let mut non_script_errors = vec![]; diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index 0e2aabc5..d16b8ea8 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -44,6 +44,16 @@ pub enum DecodingResult { Incomplete(Reject), } +impl Encode<()> for DecodingResult { + fn encode( + &self, + e: &mut Encoder, + ctx: &mut (), + ) -> Result<(), encode::Error> { + todo!() + } +} + /// An implementor of this trait is able to decode an entity from CBOR with bytes that are split /// over multiple payloads. pub trait DecodeCBORSplitPayload { From 1a79551f4c43369cc201c0278de2a3a97606009e Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Thu, 25 Jul 2024 18:08:49 +1000 Subject: [PATCH 06/15] Impl Clone for cardano node errors --- .../localtxsubmission/cardano_node_errors.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index 3c4c44e2..bc95d2b2 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -141,7 +141,7 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TxApplyErrors { pub non_script_errors: Vec, } @@ -225,7 +225,7 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Top level type for ledger errors pub enum ShelleyLedgerPredFailure { UtxowFailure(BabbageUtxowPredFailure), @@ -278,7 +278,7 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { } #[allow(clippy::enum_variant_names)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum BabbageUtxowPredFailure { AlonzoInBabbageUtxowPredFailure(AlonzoUtxowPredFailure), UtxoFailure(BabbageUtxoPredFailure), @@ -319,7 +319,7 @@ impl Decode<'_, NodeErrorDecoder> for BabbageUtxowPredFailure { } #[allow(clippy::enum_variant_names)] -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum BabbageUtxoPredFailure { AlonzoInBabbageUtxoPredFailure(AlonzoUtxoPredFailure), IncorrectTotalCollateralField, @@ -354,7 +354,7 @@ impl Decode<'_, NodeErrorDecoder> for BabbageUtxoPredFailure { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum AlonzoUtxoPredFailure { BadInputsUtxo(Vec), OutsideValidityIntervalUTxO, @@ -434,7 +434,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxoPredFailure { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum AlonzoUtxosPredFailure { ValidationTagMismatch { is_valid: bool, @@ -482,7 +482,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxosPredFailure { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum TagMismatchDescription { PassUnexpectedly, FailUnexpectedly(Vec), @@ -521,7 +521,7 @@ impl Decode<'_, NodeErrorDecoder> for TagMismatchDescription { } } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FailureDescription { pub description: String, /// Hex-encoded base64 representation of the Plutus context @@ -570,7 +570,7 @@ impl Decode<'_, NodeErrorDecoder> for FailureDescription { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum AlonzoUtxowPredFailure { ShelleyInAlonzoUtxowPredfailure(ShelleyUtxowPredFailure), MissingRedeemers, @@ -628,7 +628,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxowPredFailure { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ShelleyUtxowPredFailure { InvalidWitnessesUTXOW, /// Witnesses which failed in verifiedWits function @@ -692,7 +692,7 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyUtxowPredFailure { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TxInput { pub tx_hash: TxHash, pub index: u64, From c3796c6afbc1dd72e7200437b0d05d5950bde1d0 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Fri, 26 Jul 2024 03:56:10 +1000 Subject: [PATCH 07/15] Fix bug in protocol state --- pallas-network/src/miniprotocols/localtxsubmission/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index 2ae432dc..8162a5bc 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -190,9 +190,9 @@ where /// Returns an error if the agency is not ours or if the outbound state is /// invalid. async fn send_submit_tx(&mut self, tx: Tx) -> Result<(), Error> { - self.state = State::Busy; let msg = DecodingResult::Complete(Message::SubmitTx(tx)); self.send_message(&msg).await?; + self.state = State::Busy; debug!("sent SubmitTx"); From f4445c068ca41ef24484ae55ada4c19b68faeeac Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:54:41 +1000 Subject: [PATCH 08/15] Impl Encode for DecodingResult --- .../src/miniprotocols/localtxsubmission/codec.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index d16b8ea8..63cd1058 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -39,18 +39,22 @@ where } #[derive(Debug)] -pub enum DecodingResult { - Complete(Reject), - Incomplete(Reject), +pub enum DecodingResult { + Complete(Entity), + Incomplete(Entity), } -impl Encode<()> for DecodingResult { +impl> Encode<()> for DecodingResult { fn encode( &self, e: &mut Encoder, - ctx: &mut (), + _ctx: &mut (), ) -> Result<(), encode::Error> { - todo!() + match self { + DecodingResult::Complete(errors) | DecodingResult::Incomplete(errors) => { + errors.encode(e, _ctx) + } + } } } From 266ef7973ce5e4fae6d49f1775caa049f4eb4e17 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Sun, 28 Jul 2024 17:58:29 +1000 Subject: [PATCH 09/15] Cleanup code --- examples/n2c-miniprotocols/src/main.rs | 51 +++++++------------ .../miniprotocols/localtxsubmission/client.rs | 24 +++------ 2 files changed, 25 insertions(+), 50 deletions(-) diff --git a/examples/n2c-miniprotocols/src/main.rs b/examples/n2c-miniprotocols/src/main.rs index ab251af1..a52ef598 100644 --- a/examples/n2c-miniprotocols/src/main.rs +++ b/examples/n2c-miniprotocols/src/main.rs @@ -15,39 +15,29 @@ use pallas::{ }; use tracing::info; -async fn do_localstate_query<'a>( - mut client: NodeClient<'a, NodeErrorDecoder>, -) -> NodeClient<'a, NodeErrorDecoder> { - let localstate_client = client.statequery(); +async fn do_localstate_query(client: &mut NodeClient<'_, NodeErrorDecoder>) { + let client = client.statequery(); - localstate_client.acquire(None).await.unwrap(); + client.acquire(None).await.unwrap(); - let result = queries_v16::get_chain_point(localstate_client) - .await - .unwrap(); + let result = queries_v16::get_chain_point(client).await.unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_system_start(localstate_client) - .await - .unwrap(); + let result = queries_v16::get_system_start(client).await.unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_chain_block_no(localstate_client) - .await - .unwrap(); + let result = queries_v16::get_chain_block_no(client).await.unwrap(); info!("result: {:?}", result); - let era = queries_v16::get_current_era(localstate_client) - .await - .unwrap(); + let era = queries_v16::get_current_era(client).await.unwrap(); info!("result: {:?}", era); - let result = queries_v16::get_block_epoch_number(localstate_client, era) + let result = queries_v16::get_block_epoch_number(client, era) .await .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_stake_distribution(localstate_client, era) + let result = queries_v16::get_stake_distribution(client, era) .await .unwrap(); info!("result: {:?}", result); @@ -63,40 +53,33 @@ async fn do_localstate_query<'a>( let addry: Addr = addry.to_vec().into(); let addrs: Addrs = vec![addrx, addry]; - let result = queries_v16::get_utxo_by_address(localstate_client, era, addrs) + let result = queries_v16::get_utxo_by_address(client, era, addrs) .await .unwrap(); info!("result: {:?}", result); - let result = queries_v16::get_current_pparams(localstate_client, era) - .await - .unwrap(); + let result = queries_v16::get_current_pparams(client, era).await.unwrap(); println!("result: {:?}", result); // Stake pool ID/verification key hash (either Bech32-decoded or hex-decoded). // Empty Set means all pools. let pools: BTreeSet = BTreeSet::new(); - let result = queries_v16::get_stake_snapshots(localstate_client, era, pools) + let result = queries_v16::get_stake_snapshots(client, era, pools) .await .unwrap(); println!("result: {:?}", result); - let result = queries_v16::get_genesis_config(localstate_client, era) - .await - .unwrap(); + let result = queries_v16::get_genesis_config(client, era).await.unwrap(); println!("result: {:?}", result); // Ensure decoding across version disparities by always receiving a valid // response using the wrap function for the query result with CBOR-in-CBOR // concept. let query = queries_v16::BlockQuery::GetCurrentPParams; - let result = queries_v16::get_cbor(localstate_client, era, query) - .await - .unwrap(); + let result = queries_v16::get_cbor(client, era, query).await.unwrap(); println!("result: {:?}", result); - localstate_client.send_release().await.unwrap(); - client + client.send_release().await.unwrap(); } async fn do_chainsync<'a>(client: &'a mut NodeClient<'a, NodeErrorDecoder>) { @@ -143,7 +126,7 @@ async fn main() { // we connect to the unix socket of the local node. Make sure you have the right // path for your environment - let client = NodeClient::connect( + let mut client = NodeClient::connect( SOCKET_PATH, PRE_PRODUCTION_MAGIC, NodeErrorDecoder::default(), @@ -153,7 +136,7 @@ async fn main() { // execute an arbitrary "Local State" query against the node - let mut client = do_localstate_query(client).await; + do_localstate_query(&mut client).await; // execute the chainsync flow from an arbitrary point in the chain do_chainsync(&mut client).await; diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index 8162a5bc..c9aa5dd3 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -164,12 +164,11 @@ where async fn recv_message(&mut self) -> Result>, Error> { self.assert_agency_is_theirs()?; - let msg = { - self.muxer - .recv_full_msg() - .await - .map(DecodingResult::Complete)? - }; + let msg = self + .muxer + .recv_full_msg() + .await + .map(DecodingResult::Complete)?; self.assert_inbound_state(&msg)?; match (&self.state, &msg) { ( @@ -206,24 +205,17 @@ where async fn recv_submit_tx_response(&mut self) -> Result, Error> { debug!("waiting for SubmitTx response"); - let mut set_idle = false; - let response = match self.recv_message().await? { + match self.recv_message().await? { DecodingResult::Complete(Message::AcceptTx) => { - set_idle = true; + self.state = State::Idle; Ok(Response::Accepted) } DecodingResult::Complete(Message::RejectTx(rejection)) => { - set_idle = true; + self.state = State::Idle; Ok(Response::Rejected(rejection)) } _ => Err(Error::InvalidInbound), - }; - - if set_idle { - self.state = State::Idle; } - - response } } From a9e102d0308af4003777cc20e575eb308e7a5d04 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Sun, 28 Jul 2024 18:47:08 +1000 Subject: [PATCH 10/15] Remove RejectReason + cleanup --- .../localtxsubmission/cardano_node_errors.rs | 95 +++++----- .../miniprotocols/localtxsubmission/client.rs | 2 +- .../miniprotocols/localtxsubmission/codec.rs | 171 +----------------- .../localtxsubmission/protocol.rs | 5 - 4 files changed, 45 insertions(+), 228 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index bc95d2b2..067b7a81 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -1,6 +1,6 @@ use pallas_codec::minicbor::{ self, - data::{Int, Type}, + data::Type, decode::{self, Error, Token}, Decode, Decoder, Encode, }; @@ -149,10 +149,11 @@ pub struct TxApplyErrors { impl Encode<()> for TxApplyErrors { fn encode( &self, - e: &mut minicbor::Encoder, - ctx: &mut (), + _e: &mut minicbor::Encoder, + _ctx: &mut (), ) -> Result<(), minicbor::encode::Error> { - todo!() + // We only ever decode node errors. + unreachable!() } } @@ -167,13 +168,7 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { } } - println!( - "1111111, buf_len: {}, position: {}", - d.input().len(), - d.position() - ); expect_definite_array(vec![2], d, ctx)?; - println!("2222222"); let tag = expect_u8(d, ctx)?; assert_eq!(tag, 2); expect_definite_array(vec![1], d, ctx)?; @@ -185,7 +180,6 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { // Here we expect an indefinite array expect_indefinite_array(d, ctx)?; while let Ok(t) = d.datatype() { - println!("type: {:?}", t); if let Type::Break = t { // Here we have a clean decoding of TXApplyErrors d.skip()?; @@ -211,7 +205,6 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { ctx.cbor_break_token_seen = false; return Ok(Self { non_script_errors }); } else if e.is_end_of_input() { - //return Err(Error::message("TxApplyErrors::decode: Not enough bytes")); return Err(e); } @@ -240,10 +233,6 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { } clear_unknown_entity(d, &mut ctx.context_stack)?; } - println!( - "ShelleyLedgerPredFailure::decode inside: CTX {:?}", - ctx.context_stack - ); match expect_u8(d, ctx) { Ok(tag) => match tag { 0 => match BabbageUtxowPredFailure::decode(d, ctx) { @@ -817,28 +806,6 @@ fn expect_bytes(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result, } } -fn expect_int(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { - match d.probe().int() { - Ok(i) => { - if let Some(OuterScope::Definite(n)) = ctx.context_stack.pop() { - if n > 1 { - ctx.context_stack.push(OuterScope::Definite(n - 1)); - } - } - let _ = d.int()?; - Ok(i) - } - Err(e) => { - if e.is_end_of_input() { - Err(e) - } else { - add_collection_token_to_context(d, ctx)?; - Err(Error::message("expected int")) - } - } - } -} - fn expect_definite_array( possible_lengths: Vec, d: &mut Decoder, @@ -1014,10 +981,8 @@ pub enum OuterScope { } fn clear_unknown_entity(decoder: &mut Decoder, stack: &mut Vec) -> Result<(), Error> { - println!("Clear stack: {:?}", stack); while let Some(e) = stack.pop() { let t = next_token(decoder)?; - println!("Next token: {:?}", t); match e { OuterScope::Definite(num_left) => { @@ -1151,8 +1116,23 @@ mod tests { } } - const SPLASH_DAO_EXAMPLE: &str = "82028182059f820082018200820281581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18ef82008201820783000001000300820082018200820181581c28c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382382008201820581581c0bbd6545f014f95a65b9df462088c6600d9b2bb6cee3fe20b53241ea820082028201820782018182038201825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e88018200820282018305821a002dc6c0a2581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18efa15820378d0caaaa3855f1b38693c1d6ef004fd118691c95c959d4efa950d6d6fcf7c101821a00765cada1581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000820082028201820081825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e880182018201a1581de028c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382300ff"; - const SPLASH_BOT_EXAMPLE: &str = "82028182059f820082018207830000000100028200820282018207820181820382018258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f500820082028201830500821a06760d80a1581cfd10da3e6a578708c877e14b6aaeda8dc3a36f666a346eec52a30b3aa14974657374746f6b656e1a0001fbd08200820282018200838258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f5008258205f85cf7db4713466bc8d9d32a84b5b6bfd2f34a76b5f8cf5a5cb04b4d6d6f0380082582096eb39b8d909373c8275c611fae63792f5e3d0a67c1eee5b3afb91fdcddc859100ff"; + const NON_SCRIPT_ERROR_0: &str = "82028182059f820082018200820281581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18ef82008201820783000001000300820082018200820181581c28c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382382008201820581581c0bbd6545f014f95a65b9df462088c6600d9b2bb6cee3fe20b53241ea820082028201820782018182038201825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e88018200820282018305821a002dc6c0a2581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000581cfdaaeb99e53be5f626fb210239ece94127401d7f395a097d0a5d18efa15820378d0caaaa3855f1b38693c1d6ef004fd118691c95c959d4efa950d6d6fcf7c101821a00765cada1581cadf2425c138138efce80fd0b2ed8f227caf052f9ec44b8a92e942dfaa14653504c4153481b00001d1a94a20000820082028201820081825820e54d54359cd0da7b5ee800c3c83b3f108894d4ef76bde10df66f87c429600e880182018201a1581de028c58c07ecd2012c6c683b44ce9691ea9b0fdb9b868125a2ac29382300ff"; + const NON_SCRIPT_ERROR_1: &str = "82028182059f820082018207830000000100028200820282018207820181820382018258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f500820082028201830500821a06760d80a1581cfd10da3e6a578708c877e14b6aaeda8dc3a36f666a346eec52a30b3aa14974657374746f6b656e1a0001fbd08200820282018200838258200faddf00919ef15d38ac07684199e69be95a003a15f757bf77701072b050c1f5008258205f85cf7db4713466bc8d9d32a84b5b6bfd2f34a76b5f8cf5a5cb04b4d6d6f0380082582096eb39b8d909373c8275c611fae63792f5e3d0a67c1eee5b3afb91fdcddc859100ff"; + const NON_SCRIPT_ERROR_2: &str = + "82028182059f820082018200820a81581c3b890fb5449baedf5342a48ee9c9ec6acbc995641be92ad21f08c686\ + 8200820183038158202628ce6ff8cc7ff0922072d930e4a693c17f991748dedece0be64819a2f9ef7782582031d\ + 54ce8d7e8cb262fc891282f44e9d24c3902dc38fac63fd469e8bf3006376b5820750852fdaf0f2dd724291ce007\ + b8e76d74bcf28076ed0c494cd90c0cfe1c9ca582008201820782000000018200820183048158201a547638b4cf4\ + a3cec386e2f898ac6bc987fadd04277e1d3c8dab5c505a5674e8158201457e4107607f83a80c3c4ffeb70910c2b\ + a3a35cf1699a2a7375f50fcc54a931820082028201830500821a00636185a2581c6f1a1f0c7ccf632cc9ff4b796\ + 87ed13ffe5b624cce288b364ebdce50a144414749581b000000032a9f8800581c795ecedb09821cb922c13060c8\ + f6377c3344fa7692551e865d86ac5da158205399c766fb7c494cddb2f7ae53cc01285474388757bc05bd575c14a\ + 713a432a901820082028201820085825820497fe6401e25733c073c01164c7f2a1a05de8c95e36580f9d1b05123\ + 70040def028258207911ba2b7d91ac56b05ea351282589fe30f4717a707a1b9defaf282afe5ba44200825820791\ + 1ba2b7d91ac56b05ea351282589fe30f4717a707a1b9defaf282afe5ba44201825820869bcb6f35e6b7912c25e5\ + cb33fb9906b097980a83f2b8ef40b51c4ef52eccd402825820efc267ad2c15c34a117535eecc877241ed836eb3e\ + 643ec90de21ca1b12fd79c20282008202820181148200820283023a000f0f6d1a004944ce820082028201830d3a\ + 000f0f6d1a00106253820082028201830182811a02409e10811a024138c01a0255e528ff"; fn encode_trace() -> Result, Error> { let mut buffer = repeat(0).take(24).collect_vec(); @@ -1187,8 +1167,8 @@ mod tests { } #[test] - fn test_decode_splash_bot_example() { - let bytes = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + fn test_decode_non_script_error_0() { + let bytes = hex::decode(NON_SCRIPT_ERROR_0).unwrap(); let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); @@ -1201,8 +1181,8 @@ mod tests { } #[test] - fn test_decode_splash_dao_example() { - let bytes = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + fn test_decode_non_script_error_1() { + let bytes = hex::decode(NON_SCRIPT_ERROR_1).unwrap(); let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); @@ -1214,6 +1194,17 @@ mod tests { } } + #[test] + fn test_decode_non_script_error_2() { + let bytes = hex::decode(NON_SCRIPT_ERROR_2).unwrap(); + let mut cc = NodeErrorDecoder::new(); + let result = cc.try_decode_with_new_bytes(&bytes); + matches!( + result, + Ok(DecodingResult::Complete(Message::RejectTx(_errors))), + ); + } + #[derive(Debug, PartialEq, Eq)] struct ScriptError { error_description: String, @@ -1268,8 +1259,8 @@ mod tests { #[test] fn combined_splash_errors() { - let mut bytes = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); - bytes.extend_from_slice(&hex::decode(SPLASH_DAO_EXAMPLE).unwrap()); + let mut bytes = hex::decode(NON_SCRIPT_ERROR_1).unwrap(); + bytes.extend_from_slice(&hex::decode(NON_SCRIPT_ERROR_0).unwrap()); let mut cc = NodeErrorDecoder::new(); let result = cc.try_decode_with_new_bytes(&bytes); @@ -1286,9 +1277,9 @@ mod tests { fn neat_split_combined_splash_errors() { // We have 2 node errors side-by-side, where each error's bytes are cut in half // for partial processing. - let mut bot_bytes_0 = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + let mut bot_bytes_0 = hex::decode(NON_SCRIPT_ERROR_1).unwrap(); let bot_bytes_1 = bot_bytes_0.split_off(bot_bytes_0.len() / 2); - let mut dao_bytes_0 = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + let mut dao_bytes_0 = hex::decode(NON_SCRIPT_ERROR_0).unwrap(); let dao_bytes_1 = dao_bytes_0.split_off(dao_bytes_0.len() / 2); let mut cc = NodeErrorDecoder::new(); @@ -1331,10 +1322,10 @@ mod tests { // We have 2 node errors side-by-side, where each error's bytes are cut in half // but this is followed by cutting off a part of the end of the first error and // prepending it to the 2nd error. - let mut bot_bytes_0 = hex::decode(SPLASH_BOT_EXAMPLE).unwrap(); + let mut bot_bytes_0 = hex::decode(NON_SCRIPT_ERROR_1).unwrap(); let mut bot_bytes_1 = bot_bytes_0.split_off(bot_bytes_0.len() / 2); let mut bot_bytes_2 = bot_bytes_1.split_off(bot_bytes_1.len() / 4); - let mut dao_bytes_0 = hex::decode(SPLASH_DAO_EXAMPLE).unwrap(); + let mut dao_bytes_0 = hex::decode(NON_SCRIPT_ERROR_0).unwrap(); let dao_bytes_1 = dao_bytes_0.split_off(dao_bytes_0.len() / 2); bot_bytes_2.extend(dao_bytes_0); diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index c9aa5dd3..79c18e4d 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -7,7 +7,7 @@ use pallas_codec::minicbor; use tracing::error; use tracing::trace; -use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason, State}; +use crate::miniprotocols::localtxsubmission::{EraTx, Message, State}; use crate::multiplexer; use crate::multiplexer::AgentChannel; use crate::multiplexer::MAX_SEGMENT_PAYLOAD_LENGTH; diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index 63cd1058..b5041452 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -1,7 +1,7 @@ use pallas_codec::minicbor::data::Tag; use pallas_codec::minicbor::{decode, encode, Decode, Decoder, Encode, Encoder}; -use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason}; +use crate::miniprotocols::localtxsubmission::{EraTx, Message}; use super::cardano_node_errors::TxApplyErrors; @@ -106,172 +106,3 @@ impl Encode<()> for EraTx { Ok(()) } } - -impl<'b> Decode<'b, ()> for RejectReason { - fn decode(d: &mut Decoder<'b>, _ctx: &mut ()) -> Result { - let remainder = d.input().to_vec(); - Ok(RejectReason(remainder)) - } -} - -impl Encode<()> for RejectReason { - fn encode( - &self, - e: &mut Encoder, - _ctx: &mut (), - ) -> Result<(), encode::Error> { - e.writer_mut() - .write_all(&self.0) - .map_err(encode::Error::write)?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use pallas_codec::minicbor::{decode, Decode}; - use pallas_codec::{minicbor, Fragment}; - - use crate::miniprotocols::localtxsubmission::{EraTx, Message, RejectReason}; - use crate::multiplexer::Error; - - use super::{DecodeCBORSplitPayload, DecodingResult}; - - struct CBORDecoder; - - impl DecodeCBORSplitPayload for CBORDecoder { - type Entity = RejectReason; - - fn try_decode_with_new_bytes( - &mut self, - bytes: &[u8], - ) -> Result, decode::Error> { - let mut decoder = minicbor::Decoder::new(bytes); - let reason = RejectReason::decode(&mut decoder, &mut ()); - match reason { - Ok(reason) => Ok(DecodingResult::Complete(reason)), - Err(e) => { - unreachable!() - } - } - } - - fn has_undecoded_bytes(&self) -> bool { - false - } - } - - #[test] - fn decode_reject_message() { - let bytes = hex::decode(RAW_REJECT_RESPONSE).unwrap(); - let mut decoder = minicbor::Decoder::new(&bytes); - //let _maybe_msg: DecodingResult> = - //decoder.decode_with(&mut CBORDecoder).unwrap(); - } - - fn try_decode_message(buffer: &mut Vec) -> Result, Error> - where - M: Fragment, - { - let mut decoder = minicbor::Decoder::new(buffer); - let maybe_msg = decoder.decode(); - - match maybe_msg { - Ok(msg) => { - let pos = decoder.position(); - buffer.drain(0..pos); - Ok(Some(msg)) - } - Err(err) if err.is_end_of_input() => Ok(None), - Err(err) => Err(Error::Decoding(err.to_string())), - } - } - - #[test] - fn decode_reject_string_message() { - // let mut bytes = hex::decode(RAW_REJECT_REPONSE_ERROR_STRING).unwrap(); - // let msg_res = - // try_decode_message::>>(&mut bytes); - // println!("result {:?}", msg_res); - // assert!(msg_res.is_ok()) - } - - const RAW_REJECT_RESPONSE: &str = - "82028182059f820082018200820a81581c3b890fb5449baedf5342a48ee9c9ec6acbc995641be92ad21f08c686\ - 8200820183038158202628ce6ff8cc7ff0922072d930e4a693c17f991748dedece0be64819a2f9ef7782582031d\ - 54ce8d7e8cb262fc891282f44e9d24c3902dc38fac63fd469e8bf3006376b5820750852fdaf0f2dd724291ce007\ - b8e76d74bcf28076ed0c494cd90c0cfe1c9ca582008201820782000000018200820183048158201a547638b4cf4\ - a3cec386e2f898ac6bc987fadd04277e1d3c8dab5c505a5674e8158201457e4107607f83a80c3c4ffeb70910c2b\ - a3a35cf1699a2a7375f50fcc54a931820082028201830500821a00636185a2581c6f1a1f0c7ccf632cc9ff4b796\ - 87ed13ffe5b624cce288b364ebdce50a144414749581b000000032a9f8800581c795ecedb09821cb922c13060c8\ - f6377c3344fa7692551e865d86ac5da158205399c766fb7c494cddb2f7ae53cc01285474388757bc05bd575c14a\ - 713a432a901820082028201820085825820497fe6401e25733c073c01164c7f2a1a05de8c95e36580f9d1b05123\ - 70040def028258207911ba2b7d91ac56b05ea351282589fe30f4717a707a1b9defaf282afe5ba44200825820791\ - 1ba2b7d91ac56b05ea351282589fe30f4717a707a1b9defaf282afe5ba44201825820869bcb6f35e6b7912c25e5\ - cb33fb9906b097980a83f2b8ef40b51c4ef52eccd402825820efc267ad2c15c34a117535eecc877241ed836eb3e\ - 643ec90de21ca1b12fd79c20282008202820181148200820283023a000f0f6d1a004944ce820082028201830d3a\ - 000f0f6d1a00106253820082028201830182811a02409e10811a024138c01a0255e528ff"; - - const RAW_REJECT_REPONSE_ERROR_STRING: &str = - "6867475972786f4141794e6847514d734151455a412b675a416a734141526b4436426c65635151424751506f47\ - 4341614141484b64686b6f3677515a576467595a426c5a3242686b47566e594747515a576467595a426c5a32426\ - 86b47566e59474751595a42686b47566e594747515a5446455949426f4141717a364743415a7456454547674144\ - 5978555a4166384141526f4141567731474341614141655864526b3239415143476741432f35516141416271654\ - 26a63414145424751506f47572f324241496141414f3943426f414130374647443442476741514c67385a4d536f\ - 42476741444c6f415a4161554247674143326e675a412b675a7a775942476741424f6a515949426d6f385267674\ - 751506f47434161414145367241455a34554d454751506f43686f414177495a474a77424767414441686b596e41\ - 456141414d6766426b423251456141414d7741426b422f77455a7a504d5949426e395142676747662f564743415\ - a5742345949426c4173786767476741424b74385949426f4141762b5547674147366e67593341414241526f4141\ - 512b534753326e4141455a3672735949426f4141762b5547674147366e67593341414241526f4141762b5547674\ - 147366e67593341414241526f4145624973476741462f64344141686f414446424f4758635342426f4148577232\ - 47674142516c73454767414544475941424141614141465071786767476741444932455a4179774241526d67336\ - 86767476741445058595949426c353942676747582b344743415a7156305949426c3939786767475a5771474341\ - 6141694f737a416f61413354326b786c4b48776f61416c466568426d417377714347674149466c41614364577a5\ - 1466b452f466b452b514541414449794d6a49794d6a49794d6a49794d6a49794d6a49794d6a49794d69496c4d7a\ - 41554d6a49794d6a49794d6a49794d6a49794d6a49794d6a49794d6a49794d6a49794d6a49794d6c4d7a41774d3\ - 3447041424142435a47526b706d59475a6d3464544d774d7a4e773575744d4451774e5144306741425341414649\ - 414a494141564d774a444e77356d425341555947674469514151715a6753475a455a75764e30356763414247366\ - 3774f414154413041454d44514167564d774a444e77356d59475245536d5a675941416941454a6d41475a754141\ - 435341434d44674146494141424e494151564d774a444e784a75744d4451774e5146674168557a416b4142457a4\ - d7949694d33456d62677a4e77526d34497a4174414f4144414341424d33414762676a4d433041344152494e4150\ - 4d3342414241416d626741426b67416a413041654d4451423033576d426f417362725441304162457a496a4d6a4\ - 131496c4d7a41794142464b41715a6d42775a75764d446b4145414d556f69594152676441416d366b4145414933\ - 5747426f5a4742735947786762474273594777414a6761674b473634774e4147464d794d774d77415253695a475\ - 26b706d424d5a75504e31786762474275414562726a41324d44634145544e783575754d44594149335847427341\ - 435947344535676241416d426d41344a6d5a6d52455247526b5a754a4d33416d6267544170414e4e316f414a6d3\ - 44d7a6345414b4149414759464943616d5a676347526b706d42575a75504e317867646d4234414562726a41374d\ - 44774145544e783575754d447341493358474232414359486746686764674443627141425241424e31435141426\ - 75a674f6d3630774d77465141546461594759444a75744d444d4268544d774d5449794d6c4d774a544e78357575\ - 4d4455774e67416a646359477067624141695a75504e317867616742473634774e5141544132416d4d445541457\ - 74d6747784d33426d6267674154646159475143356d344533576d426b4175627254417941594541457a63435a67\ - 54414647426941305a6754414347426941304c47426d4145594651414a75714d4334774c77475464575a4742635\ - 94635675941416d4261594677414a675841416d59453575744d43734163416f33566d5267566d425959466f414a\ - 67564742574143594659414a6d4249627254416f414641484d7949794d6a4a544d774b7a49794d6c4d7a41754d3\ - 3447041424142435a47526b706d59474a6d34644941414149556f435a75764e30344168756e41424d4451414977\ - 4b7741546455414f4a6b5a47536d5a67596d626830674167416853674a6d3638335467434736634145774e41416\ - a417241424e3151413567596742474251414362716741524143466a49794d6a4979557a4d43387a634f6b414141\ - 454a6b5a47526b706d59475a6d346449414141495449794d6a4a544d774e7a4e77365141514151734a6d3656494\ - 1414145774f67416a417841424e3151414a676141416978676241424742614143627167415441774142457a644b\ - 6b414542555947514152675567416d366f4145774c4441744d43344145774b7a417441464e315a6b5a47536d5a6\ - 75747626830674167416859564d7a41734d33486d3634774c51415142684d4330774c6a41764148466a41764143\ - 4d43594145335641416d526756474259414359464a675667426d3634774a77437a416e414b4d77496a646159457\ - 741494168675441416d424b414359456f434275734d434941493357474243414559454a675167416d4243594434\ - 4252675067416d4138414359446f414a674f41416d41324143594451414a674d67416d417741435944414168674\ - c6741696b7773536d5a67466741696b41414a6d59434a6d3638774454415341424e3149417875744d425577456a\ - 6457594370674a414170414145526d59434941514149415970514d33537041414759414a757041434d774154645\ - 341454151726f45695141694d6a4d774241417a6463594277414a75754d413477447741544150414249694d7a4d\ - 4151414a4941416a4d7a414641435341416461627177415141794d4149335567416b52455a674645536d5a67446\ - 74169414b4b6d5a67476d62727a414a4d4134414541595441454d424577446741524d414977447741514156567a\ - 3658726756584f6b536d5a67436d626941416b67414259544d414d41494145774153496c4d7a41464d334467424\ - a41414359417741496d59415a6d3445414a49414977427741534d6a4143497a414341434142497741694d774167\ - 416741566330726f56644552674247366f4146566338474432486d6632486d665145442f32486d6657427950377\ - 9303042345a5a53547a68596162482b3653316176373668545570616c644439705748524546425245482f32486d\ - 66574279694c7235587846304c3437704c363870616e5568337443312f32484c7a313042425436456b544546425\ - 245466651555242583035475650385a412b5562414256704b5a4c365955776241574e466546324b414142594848\ - 6b6743687a624c72495933354279415a653538786c3365776836586b464d693035332b4b2f59655a3959484b303\ - 465644c505031447a4441647969454d6e77445879736a4d4769693351475346574e62722f476773764b4d416141\ - 58764a4d502f59655a3842414145412f3968356e3968356e352f59655a2f59655a2f59655a39594942364f54504\ - 845657a426d5249524448705765462b4d69394961367935426b564665434675786155714d522f77442f32486d66\ - 32486d66324871665742776d474f6c4d32775a354c7757756d78374869774978394c66304956736254505575593\ - 04c652f39683667502b68514b4641476774734d63445965352f59655a2f59"; -} diff --git a/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs b/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs index 537ff267..80b7e5ef 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/protocol.rs @@ -16,8 +16,3 @@ pub enum Message { // The bytes of a transaction with an era number. #[derive(Debug, Clone, Eq, PartialEq)] pub struct EraTx(pub u16, pub Vec); - -/// Raw reject reason, as CBOR bytes. Note that the given bytes may not represent a complete error -/// response, as the multiplexer's segment length is at most `MAX_SEGMENT_PAYLOAD_LENGTH` bytes. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct RejectReason(pub Vec); From 1c4358989f3963c8415a45f8ec6c7d7c456f809d Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Sun, 28 Jul 2024 19:05:06 +1000 Subject: [PATCH 11/15] Add links to 'cardano-ledger' for error types --- .../localtxsubmission/cardano_node_errors.rs | 37 +++++++++++++------ .../miniprotocols/localtxsubmission/client.rs | 4 +- .../miniprotocols/localtxsubmission/codec.rs | 6 +-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index 067b7a81..c7395433 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -52,7 +52,7 @@ impl Default for NodeErrorDecoder { } impl DecodeCBORSplitPayload for NodeErrorDecoder { - type Entity = Message>; + type Entity = Message>; fn try_decode_with_new_bytes( &mut self, @@ -65,7 +65,7 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { let mut errors = vec![]; loop { - match TxApplyErrors::decode(&mut decoder, self) { + match ApplyTxError::decode(&mut decoder, self) { Ok(tx_err) => { errors.push(tx_err); } @@ -110,7 +110,7 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { let mut errors = vec![]; loop { - match TxApplyErrors::decode(&mut decoder, self) { + match ApplyTxError::decode(&mut decoder, self) { Ok(tx_err) => { errors.push(tx_err); } @@ -141,12 +141,13 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/shelley/impl/src/Cardano/Ledger/Shelley/API/Mempool.hs#L221 #[derive(Debug, Clone)] -pub struct TxApplyErrors { - pub non_script_errors: Vec, +pub struct ApplyTxError { + pub node_errors: Vec, } -impl Encode<()> for TxApplyErrors { +impl Encode<()> for ApplyTxError { fn encode( &self, _e: &mut minicbor::Encoder, @@ -157,7 +158,7 @@ impl Encode<()> for TxApplyErrors { } } -impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { +impl Decode<'_, NodeErrorDecoder> for ApplyTxError { fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { let mut non_script_errors = vec![]; @@ -185,7 +186,9 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { d.skip()?; ctx.ix_start_unprocessed_bytes = d.position(); ctx.cbor_break_token_seen = false; - return Ok(Self { non_script_errors }); + return Ok(Self { + node_errors: non_script_errors, + }); } match ShelleyLedgerPredFailure::decode(d, ctx) { @@ -203,7 +206,9 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { // decoded. ctx.ix_start_unprocessed_bytes = d.position(); ctx.cbor_break_token_seen = false; - return Ok(Self { non_script_errors }); + return Ok(Self { + node_errors: non_script_errors, + }); } else if e.is_end_of_input() { return Err(e); } @@ -219,7 +224,7 @@ impl Decode<'_, NodeErrorDecoder> for TxApplyErrors { } #[derive(Debug, Clone)] -/// Top level type for ledger errors +/// Top level type for ledger errors. See https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Ledger.hs#L100 pub enum ShelleyLedgerPredFailure { UtxowFailure(BabbageUtxowPredFailure), DelegsFailure, @@ -266,6 +271,7 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/babbage/impl/src/Cardano/Ledger/Babbage/Rules/Utxow.hs#L97 #[allow(clippy::enum_variant_names)] #[derive(Debug, Clone)] pub enum BabbageUtxowPredFailure { @@ -307,6 +313,7 @@ impl Decode<'_, NodeErrorDecoder> for BabbageUtxowPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/babbage/impl/src/Cardano/Ledger/Babbage/Rules/Utxo.hs#L109 #[allow(clippy::enum_variant_names)] #[derive(Debug, Clone)] pub enum BabbageUtxoPredFailure { @@ -343,6 +350,7 @@ impl Decode<'_, NodeErrorDecoder> for BabbageUtxoPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxo.hs#L116 #[derive(Debug, Clone)] pub enum AlonzoUtxoPredFailure { BadInputsUtxo(Vec), @@ -357,6 +365,7 @@ pub enum AlonzoUtxoPredFailure { WrongNetwork, WrongNetworkWithdrawal, OutputTooSmallUTxO, + /// Script-failure UtxosFailure(AlonzoUtxosPredFailure), OutputBootAddrAttrsTooBig, TriesToForgeADA, @@ -423,6 +432,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxoPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxos.hs#L398 #[derive(Debug, Clone)] pub enum AlonzoUtxosPredFailure { ValidationTagMismatch { @@ -471,6 +481,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxosPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxos.hs#L367 #[derive(Debug, Clone)] pub enum TagMismatchDescription { PassUnexpectedly, @@ -510,6 +521,8 @@ impl Decode<'_, NodeErrorDecoder> for TagMismatchDescription { } } } + +// Describes script-error from the node. See: https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxos.hs#L334 #[derive(Debug, Clone)] pub struct FailureDescription { pub description: String, @@ -559,6 +572,7 @@ impl Decode<'_, NodeErrorDecoder> for FailureDescription { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxow.hs#L97 #[derive(Debug, Clone)] pub enum AlonzoUtxowPredFailure { ShelleyInAlonzoUtxowPredfailure(ShelleyUtxowPredFailure), @@ -617,6 +631,7 @@ impl Decode<'_, NodeErrorDecoder> for AlonzoUtxowPredFailure { } } +/// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Utxow.hs#L127 #[derive(Debug, Clone)] pub enum ShelleyUtxowPredFailure { InvalidWitnessesUTXOW, @@ -1110,7 +1125,7 @@ mod tests { let result = cc.try_decode_with_new_bytes(&buffer); if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { assert_eq!(errors.len(), 1); - assert_eq!(errors[0].non_script_errors.len(), 0); + assert_eq!(errors[0].node_errors.len(), 0); } else { panic!("") } diff --git a/pallas-network/src/miniprotocols/localtxsubmission/client.rs b/pallas-network/src/miniprotocols/localtxsubmission/client.rs index 79c18e4d..bb7d9ee7 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/client.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/client.rs @@ -12,12 +12,12 @@ use crate::multiplexer; use crate::multiplexer::AgentChannel; use crate::multiplexer::MAX_SEGMENT_PAYLOAD_LENGTH; -use super::cardano_node_errors::TxApplyErrors; +use super::cardano_node_errors::ApplyTxError; use super::codec::DecodeCBORSplitPayload; use super::codec::DecodingResult; /// Cardano specific instantiation of LocalTxSubmission client. -pub type Client<'a, ErrDecoder> = GenericClient<'a, EraTx, Vec, ErrDecoder>; +pub type Client<'a, ErrDecoder> = GenericClient<'a, EraTx, Vec, ErrDecoder>; /// A generic Ouroboros client for submitting a generic transaction /// to a server, which possibly results in a generic rejection. diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index b5041452..71f1382f 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -3,7 +3,7 @@ use pallas_codec::minicbor::{decode, encode, Decode, Decoder, Encode, Encoder}; use crate::miniprotocols::localtxsubmission::{EraTx, Message}; -use super::cardano_node_errors::TxApplyErrors; +use super::cardano_node_errors::ApplyTxError; impl Encode<()> for Message where @@ -72,9 +72,9 @@ pub trait DecodeCBORSplitPayload { fn has_undecoded_bytes(&self) -> bool; } -impl<'b, C> Decode<'b, C> for DecodingResult>> +impl<'b, C> Decode<'b, C> for DecodingResult>> where - C: DecodeCBORSplitPayload>>, + C: DecodeCBORSplitPayload>>, { fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result { ctx.try_decode_with_new_bytes(d.input()) From 0327349b21e69ab0aed0b99d34da2f8c2b82b03d Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Mon, 29 Jul 2024 18:56:25 +1000 Subject: [PATCH 12/15] Re-arrange local-tx-submission code --- examples/crawler/src/main.rs | 5 +- examples/n2c-miniprotocols/src/main.rs | 2 +- .../localtxsubmission/cardano_node_errors.rs | 160 ++---------------- .../miniprotocols/localtxsubmission/codec.rs | 132 ++++++++++++++- .../miniprotocols/localtxsubmission/mod.rs | 1 + pallas-network/tests/protocols.rs | 2 +- 6 files changed, 150 insertions(+), 152 deletions(-) diff --git a/examples/crawler/src/main.rs b/examples/crawler/src/main.rs index 78e85d9d..154b648a 100644 --- a/examples/crawler/src/main.rs +++ b/examples/crawler/src/main.rs @@ -6,10 +6,7 @@ use pallas::{ ledger::traverse::{MultiEraBlock, MultiEraTx}, network::{ facades::NodeClient, - miniprotocols::{ - chainsync::NextResponse, localtxsubmission::cardano_node_errors::NodeErrorDecoder, - Point, - }, + miniprotocols::{chainsync::NextResponse, localtxsubmission::NodeErrorDecoder, Point}, }, }; diff --git a/examples/n2c-miniprotocols/src/main.rs b/examples/n2c-miniprotocols/src/main.rs index a52ef598..74f5e4a8 100644 --- a/examples/n2c-miniprotocols/src/main.rs +++ b/examples/n2c-miniprotocols/src/main.rs @@ -8,7 +8,7 @@ use pallas::{ miniprotocols::{ chainsync, localstate::queries_v16::{self, Addr, Addrs}, - localtxsubmission::cardano_node_errors::NodeErrorDecoder, + localtxsubmission::NodeErrorDecoder, Point, PRE_PRODUCTION_MAGIC, }, }, diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index c7395433..71187c8f 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -1,145 +1,15 @@ +//! This modules contains Rust-equivalents of Haskell types from `cardano-ledger` representing +//! errors that are sent from the cardano node in the local-TX-submission miniprotocol. use pallas_codec::minicbor::{ self, data::Type, - decode::{self, Error, Token}, + decode::{Error, Token}, Decode, Decoder, Encode, }; use pallas_primitives::conway::ScriptHash; use pallas_utxorpc::TxHash; -use crate::miniprotocols::localtxsubmission::{codec::DecodingResult, Message}; - -use super::{codec::DecodeCBORSplitPayload, EraTx}; - -/// Decodes Cardano node errors whose CBOR byte representation could be split over multiple -/// payloads. -pub struct NodeErrorDecoder { - /// When decoding the error responses of the node, we use a stack to track the location of the - /// decoding relative to an outer scope (most often a definite array). We need it because if we - /// come across an error that we cannot handle, we must still consume all the CBOR bytes that - /// represent this error. - pub context_stack: Vec, - /// Response bytes from the cardano node. Note that there are payload limits and so the bytes - /// may be truncated. - pub response_bytes: Vec, - /// This field is used to determine if there are still CBOR bytes that have yet to be decoded. - /// - /// It has a value of 0 if decoding has not yet started. Otherwise it takes the value of the - /// index in `response_bytes` that is also pointed to by the minicbor decoder after a - /// _successful_ decoding of a `TxApplyErrors` instance. - pub ix_start_unprocessed_bytes: usize, - /// This field is true if the current decoding of a `TXApplyErrors` instance is complete, which - /// only happens once the CBOR BREAK token is decoded to terminate the indefinite array which is - /// part of the `TxApplyErrors` encoded structure. - pub cbor_break_token_seen: bool, -} - -impl NodeErrorDecoder { - pub fn new() -> Self { - Self { - context_stack: vec![], - response_bytes: vec![], - ix_start_unprocessed_bytes: 0, - cbor_break_token_seen: false, - } - } -} - -impl Default for NodeErrorDecoder { - fn default() -> Self { - Self::new() - } -} - -impl DecodeCBORSplitPayload for NodeErrorDecoder { - type Entity = Message>; - - fn try_decode_with_new_bytes( - &mut self, - bytes: &[u8], - ) -> Result, decode::Error> { - if self.has_undecoded_bytes() { - self.response_bytes.extend_from_slice(bytes); - let bytes = self.response_bytes.clone(); - let mut decoder = Decoder::new(&bytes); - let mut errors = vec![]; - - loop { - match ApplyTxError::decode(&mut decoder, self) { - Ok(tx_err) => { - errors.push(tx_err); - } - Err(e) => { - if !e.is_end_of_input() { - return Err(e); - } else { - break; - } - } - } - } - - if self.has_undecoded_bytes() { - Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) - } else { - Ok(DecodingResult::Complete(Message::RejectTx(errors))) - } - } else { - // If it's not an error response then process it right here and return. - let mut d = Decoder::new(bytes); - let mut probe = d.probe(); - if probe.array().is_err() { - // If we don't have any unprocessed bytes the first element should be an array - return Err(decode::Error::message( - "Expecting an array (no unprocessed bytes)", - )); - } - let label = probe.u16()?; - match label { - 0 => { - d.array()?; - d.u16()?; - let tx = d.decode()?; - Ok(DecodingResult::Complete(Message::SubmitTx(tx))) - } - 1 => Ok(DecodingResult::Complete(Message::AcceptTx)), - 2 => { - self.response_bytes.extend_from_slice(bytes); - let bytes = self.response_bytes.clone(); - let mut decoder = Decoder::new(&bytes); - let mut errors = vec![]; - - loop { - match ApplyTxError::decode(&mut decoder, self) { - Ok(tx_err) => { - errors.push(tx_err); - } - Err(e) => { - if !e.is_end_of_input() { - return Err(e); - } else { - break; - } - } - } - } - - if self.has_undecoded_bytes() { - Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) - } else { - Ok(DecodingResult::Complete(Message::RejectTx(errors))) - } - } - 3 => Ok(DecodingResult::Complete(Message::Done)), - _ => Err(decode::Error::message("can't decode Message")), - } - } - } - - fn has_undecoded_bytes(&self) -> bool { - self.ix_start_unprocessed_bytes + 1 < self.response_bytes.len() - } -} +use super::codec::NodeErrorDecoder; /// https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/shelley/impl/src/Cardano/Ledger/Shelley/API/Mempool.hs#L221 #[derive(Debug, Clone)] @@ -147,17 +17,6 @@ pub struct ApplyTxError { pub node_errors: Vec, } -impl Encode<()> for ApplyTxError { - fn encode( - &self, - _e: &mut minicbor::Encoder, - _ctx: &mut (), - ) -> Result<(), minicbor::encode::Error> { - // We only ever decode node errors. - unreachable!() - } -} - impl Decode<'_, NodeErrorDecoder> for ApplyTxError { fn decode(d: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result { let mut non_script_errors = vec![]; @@ -223,6 +82,17 @@ impl Decode<'_, NodeErrorDecoder> for ApplyTxError { } } +impl Encode<()> for ApplyTxError { + fn encode( + &self, + _e: &mut minicbor::Encoder, + _ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + // We only ever decode node errors. + unreachable!() + } +} + #[derive(Debug, Clone)] /// Top level type for ledger errors. See https://github.com/IntersectMBO/cardano-ledger/blob/8fd7ab6ca9bcf9cdb1fa6f4059f84585a084efa5/eras/shelley/impl/src/Cardano/Ledger/Shelley/Rules/Ledger.hs#L100 pub enum ShelleyLedgerPredFailure { diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index 71f1382f..fc90c95b 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -3,7 +3,7 @@ use pallas_codec::minicbor::{decode, encode, Decode, Decoder, Encode, Encoder}; use crate::miniprotocols::localtxsubmission::{EraTx, Message}; -use super::cardano_node_errors::ApplyTxError; +use super::cardano_node_errors::{ApplyTxError, OuterScope}; impl Encode<()> for Message where @@ -72,6 +72,136 @@ pub trait DecodeCBORSplitPayload { fn has_undecoded_bytes(&self) -> bool; } +/// Decodes Cardano node errors whose CBOR byte representation could be split over multiple +/// payloads. +pub struct NodeErrorDecoder { + /// When decoding the error responses of the node, we use a stack to track the location of the + /// decoding relative to an outer scope (most often a definite array). We need it because if we + /// come across an error that we cannot handle, we must still consume all the CBOR bytes that + /// represent this error. + pub context_stack: Vec, + /// Response bytes from the cardano node. Note that there are payload limits and so the bytes + /// may be truncated. + pub response_bytes: Vec, + /// This field is used to determine if there are still CBOR bytes that have yet to be decoded. + /// + /// It has a value of 0 if decoding has not yet started. Otherwise it takes the value of the + /// index in `response_bytes` that is also pointed to by the minicbor decoder after a + /// _successful_ decoding of a `TxApplyErrors` instance. + pub ix_start_unprocessed_bytes: usize, + /// This field is true if the current decoding of a `TXApplyErrors` instance is complete, which + /// only happens once the CBOR BREAK token is decoded to terminate the indefinite array which is + /// part of the `TxApplyErrors` encoded structure. + pub cbor_break_token_seen: bool, +} + +impl NodeErrorDecoder { + pub fn new() -> Self { + Self { + context_stack: vec![], + response_bytes: vec![], + ix_start_unprocessed_bytes: 0, + cbor_break_token_seen: false, + } + } +} + +impl Default for NodeErrorDecoder { + fn default() -> Self { + Self::new() + } +} + +impl DecodeCBORSplitPayload for NodeErrorDecoder { + type Entity = Message>; + + fn try_decode_with_new_bytes( + &mut self, + bytes: &[u8], + ) -> Result, decode::Error> { + if self.has_undecoded_bytes() { + self.response_bytes.extend_from_slice(bytes); + let bytes = self.response_bytes.clone(); + let mut decoder = Decoder::new(&bytes); + let mut errors = vec![]; + + loop { + match ApplyTxError::decode(&mut decoder, self) { + Ok(tx_err) => { + errors.push(tx_err); + } + Err(e) => { + if !e.is_end_of_input() { + return Err(e); + } else { + break; + } + } + } + } + + if self.has_undecoded_bytes() { + Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) + } else { + Ok(DecodingResult::Complete(Message::RejectTx(errors))) + } + } else { + // If it's not an error response then process it right here and return. + let mut d = Decoder::new(bytes); + let mut probe = d.probe(); + if probe.array().is_err() { + // If we don't have any unprocessed bytes the first element should be an array + return Err(decode::Error::message( + "Expecting an array (no unprocessed bytes)", + )); + } + let label = probe.u16()?; + match label { + 0 => { + d.array()?; + d.u16()?; + let tx = d.decode()?; + Ok(DecodingResult::Complete(Message::SubmitTx(tx))) + } + 1 => Ok(DecodingResult::Complete(Message::AcceptTx)), + 2 => { + self.response_bytes.extend_from_slice(bytes); + let bytes = self.response_bytes.clone(); + let mut decoder = Decoder::new(&bytes); + let mut errors = vec![]; + + loop { + match ApplyTxError::decode(&mut decoder, self) { + Ok(tx_err) => { + errors.push(tx_err); + } + Err(e) => { + if !e.is_end_of_input() { + return Err(e); + } else { + break; + } + } + } + } + + if self.has_undecoded_bytes() { + Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) + } else { + Ok(DecodingResult::Complete(Message::RejectTx(errors))) + } + } + 3 => Ok(DecodingResult::Complete(Message::Done)), + _ => Err(decode::Error::message("can't decode Message")), + } + } + } + + fn has_undecoded_bytes(&self) -> bool { + self.ix_start_unprocessed_bytes + 1 < self.response_bytes.len() + } +} + impl<'b, C> Decode<'b, C> for DecodingResult>> where C: DecodeCBORSplitPayload>>, diff --git a/pallas-network/src/miniprotocols/localtxsubmission/mod.rs b/pallas-network/src/miniprotocols/localtxsubmission/mod.rs index 3474660a..5b20b7e4 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/mod.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/mod.rs @@ -1,4 +1,5 @@ pub use client::*; +pub use codec::NodeErrorDecoder; pub use protocol::*; pub mod cardano_node_errors; diff --git a/pallas-network/tests/protocols.rs b/pallas-network/tests/protocols.rs index e7fbc496..bc0d8288 100644 --- a/pallas-network/tests/protocols.rs +++ b/pallas-network/tests/protocols.rs @@ -14,7 +14,7 @@ use pallas_network::miniprotocols::localstate::queries_v16::{ Value, }; use pallas_network::miniprotocols::localstate::ClientQueryRequest; -use pallas_network::miniprotocols::localtxsubmission::cardano_node_errors::NodeErrorDecoder; +use pallas_network::miniprotocols::localtxsubmission::NodeErrorDecoder; use pallas_network::miniprotocols::txsubmission::{EraTxBody, TxIdAndSize}; use pallas_network::miniprotocols::{ blockfetch, From 742946067695565d6c42a3c401ef1b31f27a682d Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Fri, 2 Aug 2024 23:25:20 +1000 Subject: [PATCH 13/15] Track CBOR::Break token in `clear_unknown_entity` --- .../localtxsubmission/cardano_node_errors.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index 71187c8f..e1b67569 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -106,7 +106,7 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { if e.is_end_of_input() { return Err(e); } - clear_unknown_entity(d, &mut ctx.context_stack)?; + clear_unknown_entity(d, ctx)?; } match expect_u8(d, ctx) { Ok(tag) => match tag { @@ -116,13 +116,13 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { if e.is_end_of_input() { Err(e) } else { - clear_unknown_entity(d, &mut ctx.context_stack)?; + clear_unknown_entity(d, ctx)?; Err(e) } } }, _ => { - clear_unknown_entity(d, &mut ctx.context_stack)?; + clear_unknown_entity(d, ctx)?; Err(Error::message("not ShelleyLedgerPredFailure")) } }, @@ -131,7 +131,7 @@ impl Decode<'_, NodeErrorDecoder> for ShelleyLedgerPredFailure { Err(e) } else { add_collection_token_to_context(d, ctx)?; - clear_unknown_entity(d, &mut ctx.context_stack)?; + clear_unknown_entity(d, ctx)?; Err(Error::message( "ShelleyLedgerPredFailure::decode: expected tag", )) @@ -865,30 +865,31 @@ pub enum OuterScope { Indefinite, } -fn clear_unknown_entity(decoder: &mut Decoder, stack: &mut Vec) -> Result<(), Error> { - while let Some(e) = stack.pop() { +fn clear_unknown_entity(decoder: &mut Decoder, ctx: &mut NodeErrorDecoder) -> Result<(), Error> { + while let Some(e) = ctx.context_stack.pop() { let t = next_token(decoder)?; match e { OuterScope::Definite(num_left) => { if num_left > 1 { - stack.push(OuterScope::Definite(num_left - 1)); + ctx.context_stack.push(OuterScope::Definite(num_left - 1)); } } - OuterScope::Indefinite => stack.push(OuterScope::Indefinite), + OuterScope::Indefinite => ctx.context_stack.push(OuterScope::Indefinite), } match t { Token::BeginArray | Token::BeginBytes | Token::BeginMap => { - stack.push(OuterScope::Indefinite); + ctx.context_stack.push(OuterScope::Indefinite); } Token::Array(n) | Token::Map(n) => { - stack.push(OuterScope::Definite(n)); + ctx.context_stack.push(OuterScope::Definite(n)); } Token::Break => { assert_eq!(e, OuterScope::Indefinite); - assert_eq!(stack.pop(), Some(OuterScope::Indefinite)); + assert_eq!(ctx.context_stack.pop(), Some(OuterScope::Indefinite)); + ctx.cbor_break_token_seen = true; } // Throw away the token From ef3e151e05ca599b750a2c27efceb8bb206a7268 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:17:23 +1000 Subject: [PATCH 14/15] Log raw error bytes from the node in local-tx-submission mini-protocol --- pallas-network/src/miniprotocols/localtxsubmission/codec.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index fc90c95b..d545d1d1 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -1,5 +1,6 @@ use pallas_codec::minicbor::data::Tag; use pallas_codec::minicbor::{decode, encode, Decode, Decoder, Encode, Encoder}; +use tracing::trace; use crate::miniprotocols::localtxsubmission::{EraTx, Message}; @@ -143,6 +144,10 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { if self.has_undecoded_bytes() { Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) } else { + trace!( + "cardano node raw error bytes: {}", + hex::encode(&self.response_bytes) + ); Ok(DecodingResult::Complete(Message::RejectTx(errors))) } } else { From d4189659eb742ae08e913843f4a56c8db5666737 Mon Sep 17 00:00:00 2001 From: Tim Ling <791016+kettlebell@users.noreply.github.com> Date: Tue, 20 Aug 2024 02:04:36 +1000 Subject: [PATCH 15/15] Clear byte buffer in `NodeErrorDecoder` after a complete decoding of error response --- .../localtxsubmission/cardano_node_errors.rs | 6 ++++-- .../src/miniprotocols/localtxsubmission/codec.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs index e1b67569..903bf28c 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/cardano_node_errors.rs @@ -1186,9 +1186,11 @@ mod tests { panic!(""); } + // Internal byte buffered has cleared from previous complete decoding. The incoming bytes does not + // contain a complete `ApplyTxError` instance. let result = cc.try_decode_with_new_bytes(&dao_bytes_0); if let Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) = result { - assert_eq!(errors.len(), 1); + assert_eq!(errors.len(), 0); assert!(cc.has_undecoded_bytes()); } else { panic!(""); @@ -1196,7 +1198,7 @@ mod tests { let result = cc.try_decode_with_new_bytes(&dao_bytes_1); if let Ok(DecodingResult::Complete(Message::RejectTx(errors))) = result { - assert_eq!(errors.len(), 2); + assert_eq!(errors.len(), 1); assert!(!cc.has_undecoded_bytes()); } else { panic!(""); diff --git a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs index d545d1d1..c09d7900 100644 --- a/pallas-network/src/miniprotocols/localtxsubmission/codec.rs +++ b/pallas-network/src/miniprotocols/localtxsubmission/codec.rs @@ -148,6 +148,10 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { "cardano node raw error bytes: {}", hex::encode(&self.response_bytes) ); + self.response_bytes.clear(); + self.cbor_break_token_seen = false; + self.ix_start_unprocessed_bytes = 0; + assert!(self.context_stack.is_empty()); Ok(DecodingResult::Complete(Message::RejectTx(errors))) } } else { @@ -193,6 +197,10 @@ impl DecodeCBORSplitPayload for NodeErrorDecoder { if self.has_undecoded_bytes() { Ok(DecodingResult::Incomplete(Message::RejectTx(errors))) } else { + self.response_bytes.clear(); + self.cbor_break_token_seen = false; + self.ix_start_unprocessed_bytes = 0; + assert!(self.context_stack.is_empty()); Ok(DecodingResult::Complete(Message::RejectTx(errors))) } }