fix(interop): use correct input order to match redeemers (#487)
This commit is contained in:
parent
15538cd0bf
commit
1406d7a599
8 changed files with 1917 additions and 27 deletions
|
|
@ -17,8 +17,8 @@ async fn main() {
|
|||
.unwrap();
|
||||
|
||||
let point = Point::Specific(
|
||||
49159253,
|
||||
hex::decode("d034a2d0e4c3076f57368ed59319010c265718f0923057f8ff914a3b6bfd1314").unwrap(),
|
||||
101516417,
|
||||
hex::decode("3d681e503fd9318d0f68c74a699895ce61f0a07010b516b80ce968a6b000e231").unwrap(),
|
||||
);
|
||||
|
||||
let block = peer.blockfetch().fetch_single(point).await.unwrap();
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ impl<'b> MultiEraInput<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the key used for lexicographical ordering of the input
|
||||
pub fn lexicographical_key(&self) -> String {
|
||||
format!("{}#{}", self.hash(), self.index())
|
||||
}
|
||||
|
||||
pub fn hash(&self) -> &Hash<32> {
|
||||
match self {
|
||||
MultiEraInput::Byron(x) => match x.deref().deref() {
|
||||
|
|
|
|||
|
|
@ -196,6 +196,19 @@ impl<'b> MultiEraTx<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return inputs as expected for processing
|
||||
///
|
||||
/// To process inputs we need a set (no duplicated) and lexicographical
|
||||
/// order (hash#idx). This function will take the raw inputs and apply the
|
||||
/// aforementioned cleanup changes.
|
||||
pub fn inputs_sorted_set(&self) -> Vec<MultiEraInput> {
|
||||
let mut raw = self.inputs();
|
||||
raw.sort_by_key(|x| x.lexicographical_key());
|
||||
raw.dedup_by_key(|x| x.lexicographical_key());
|
||||
|
||||
raw
|
||||
}
|
||||
|
||||
/// Return the transaction reference inputs
|
||||
///
|
||||
/// NOTE: It is possible for this to return duplicates. See
|
||||
|
|
|
|||
|
|
@ -155,6 +155,12 @@ impl<'b> MultiEraTx<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_spend_redeemer(&self, input_order: u32) -> Option<MultiEraRedeemer> {
|
||||
self.redeemers().into_iter().find(|r| {
|
||||
r.tag() == pallas_primitives::conway::RedeemerTag::Spend && r.index() == input_order
|
||||
})
|
||||
}
|
||||
|
||||
pub fn plutus_v2_scripts(&self) -> &[PlutusV2Script] {
|
||||
match self {
|
||||
Self::Byron(_) => &[],
|
||||
|
|
|
|||
|
|
@ -17,4 +17,8 @@ pallas-codec = { version = "=0.28.0", path = "../pallas-codec" }
|
|||
pallas-crypto = { version = "=0.28.0", path = "../pallas-crypto" }
|
||||
|
||||
utxorpc-spec = { version = "0.6.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.3"
|
||||
serde_json = "1.0.120"
|
||||
# utxorpc-spec = { path = "../../../utxorpc/spec/gen/rust" }
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ use std::{collections::HashMap, ops::Deref};
|
|||
|
||||
use pallas_codec::utils::KeyValuePairs;
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::{
|
||||
alonzo, babbage,
|
||||
conway::{self, RedeemerTag},
|
||||
};
|
||||
use pallas_primitives::{alonzo, babbage, conway};
|
||||
use pallas_traverse as trv;
|
||||
|
||||
use trv::OriginalHash;
|
||||
|
|
@ -57,33 +54,61 @@ impl<C: LedgerContext> Mapper<C> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map_tx_input(
|
||||
fn decode_resolved_utxo(
|
||||
&self,
|
||||
i: &trv::MultiEraInput,
|
||||
tx: &trv::MultiEraTx,
|
||||
resolved: &Option<UtxoMap>,
|
||||
) -> u5c::TxInput {
|
||||
let redeemers = tx.redeemers();
|
||||
input: &trv::MultiEraInput,
|
||||
) -> Option<u5c::TxOutput> {
|
||||
let as_txref = (*input.hash(), input.index() as u32);
|
||||
|
||||
let redeemer = redeemers
|
||||
.iter()
|
||||
.find(|r| r.tag() == RedeemerTag::Spend && (r.index() as u64) == i.index());
|
||||
|
||||
let as_txref = (*i.hash(), i.index() as u32);
|
||||
|
||||
let as_output = resolved
|
||||
resolved
|
||||
.as_ref()
|
||||
.and_then(|x| x.get(&as_txref))
|
||||
.and_then(|(era, cbor)| {
|
||||
let o = trv::MultiEraOutput::decode(*era, cbor.as_slice()).ok()?;
|
||||
Some(self.map_tx_output(&o))
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_tx_input(
|
||||
&self,
|
||||
input: &trv::MultiEraInput,
|
||||
tx: &trv::MultiEraTx,
|
||||
// lexicographical order of the input we're mapping
|
||||
order: u32,
|
||||
resolved: &Option<UtxoMap>,
|
||||
) -> u5c::TxInput {
|
||||
u5c::TxInput {
|
||||
tx_hash: i.hash().to_vec().into(),
|
||||
output_index: i.index() as u32,
|
||||
redeemer: redeemer.map(|x| self.map_redeemer(x)),
|
||||
as_output,
|
||||
tx_hash: input.hash().to_vec().into(),
|
||||
output_index: input.index() as u32,
|
||||
as_output: self.decode_resolved_utxo(resolved, input),
|
||||
redeemer: tx.find_spend_redeemer(order).map(|x| self.map_redeemer(&x)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_tx_reference_input(
|
||||
&self,
|
||||
input: &trv::MultiEraInput,
|
||||
resolved: &Option<UtxoMap>,
|
||||
) -> u5c::TxInput {
|
||||
u5c::TxInput {
|
||||
tx_hash: input.hash().to_vec().into(),
|
||||
output_index: input.index() as u32,
|
||||
as_output: self.decode_resolved_utxo(resolved, input),
|
||||
redeemer: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_tx_collateral(
|
||||
&self,
|
||||
input: &trv::MultiEraInput,
|
||||
resolved: &Option<UtxoMap>,
|
||||
) -> u5c::TxInput {
|
||||
u5c::TxInput {
|
||||
tx_hash: input.hash().to_vec().into(),
|
||||
output_index: input.index() as u32,
|
||||
as_output: self.decode_resolved_utxo(resolved, input),
|
||||
redeemer: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,9 +525,10 @@ impl<C: LedgerContext> Mapper<C> {
|
|||
u5c::Tx {
|
||||
hash: tx.hash().to_vec().into(),
|
||||
inputs: tx
|
||||
.inputs()
|
||||
.inputs_sorted_set()
|
||||
.iter()
|
||||
.map(|i| self.map_tx_input(i, tx, &resolved))
|
||||
.enumerate()
|
||||
.map(|(order, i)| self.map_tx_input(i, tx, order as u32, &resolved))
|
||||
.collect(),
|
||||
outputs: tx.outputs().iter().map(|x| self.map_tx_output(x)).collect(),
|
||||
certificates: tx.certs().iter().map(|x| self.map_cert(x)).collect(),
|
||||
|
|
@ -520,7 +546,7 @@ impl<C: LedgerContext> Mapper<C> {
|
|||
reference_inputs: tx
|
||||
.reference_inputs()
|
||||
.iter()
|
||||
.map(|x| self.map_tx_input(x, tx, &resolved))
|
||||
.map(|x| self.map_tx_reference_input(x, &resolved))
|
||||
.collect(),
|
||||
witnesses: u5c::WitnessSet {
|
||||
vkeywitness: tx
|
||||
|
|
@ -540,7 +566,7 @@ impl<C: LedgerContext> Mapper<C> {
|
|||
collateral: tx
|
||||
.collateral()
|
||||
.iter()
|
||||
.map(|x| self.map_tx_input(x, tx, &resolved))
|
||||
.map(|x| self.map_tx_collateral(x, &resolved))
|
||||
.collect(),
|
||||
collateral_return: tx.collateral_return().map(|x| self.map_tx_output(&x)),
|
||||
total_collateral: tx.total_collateral().unwrap_or_default(),
|
||||
|
|
@ -586,3 +612,34 @@ impl<C: LedgerContext> Mapper<C> {
|
|||
self.map_block(&block)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NoLedger;
|
||||
|
||||
impl LedgerContext for NoLedger {
|
||||
fn get_utxos(&self, _refs: &[TxoRef]) -> Option<UtxoMap> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot() {
|
||||
let test_blocks = [include_str!("../../test_data/u5c1.block")];
|
||||
let test_snapshots = [include_str!("../../test_data/u5c1.json")];
|
||||
|
||||
let mapper = Mapper::new(NoLedger);
|
||||
|
||||
for (block_str, json_str) in test_blocks.iter().zip(test_snapshots) {
|
||||
let cbor = hex::decode(block_str).unwrap();
|
||||
let block = pallas_traverse::MultiEraBlock::decode(&cbor).unwrap();
|
||||
let current = serde_json::json!(mapper.map_block(&block));
|
||||
let expected: serde_json::Value = serde_json::from_str(&json_str).unwrap();
|
||||
|
||||
assert_eq!(current, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
test_data/u5c1.block
Normal file
1
test_data/u5c1.block
Normal file
File diff suppressed because one or more lines are too long
1804
test_data/u5c1.json
Normal file
1804
test_data/u5c1.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue