cardano_serialization_lib/builders/
tx_inputs_builder.rsuse crate::*;
use hashlink::LinkedHashMap;
use std::collections::{BTreeMap, BTreeSet};
#[derive(Clone, Debug)]
pub(crate) struct TxBuilderInput {
pub(crate) input: TransactionInput,
pub(crate) amount: Value, }
#[derive(Clone, Debug)]
pub struct InputsRequiredWitness {
vkeys: Ed25519KeyHashes,
scripts: LinkedHashMap<ScriptHash, LinkedHashMap<TransactionInput, Option<ScriptWitnessType>>>,
bootstraps: BTreeSet<Vec<u8>>,
}
#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct TxInputsBuilder {
inputs: BTreeMap<TransactionInput, (TxBuilderInput, Option<ScriptHash>)>,
required_witnesses: InputsRequiredWitness,
}
pub(crate) fn get_bootstraps(inputs: &TxInputsBuilder) -> BTreeSet<Vec<u8>> {
inputs.required_witnesses.bootstraps.clone()
}
#[wasm_bindgen]
impl TxInputsBuilder {
pub fn new() -> Self {
Self {
inputs: BTreeMap::new(),
required_witnesses: InputsRequiredWitness {
vkeys: Ed25519KeyHashes::new(),
scripts: LinkedHashMap::new(),
bootstraps: BTreeSet::new(),
},
}
}
fn push_input(&mut self, e: (TxBuilderInput, Option<ScriptHash>)) {
self.inputs.insert(e.0.input.clone(), e);
}
pub fn add_key_input(
&mut self,
hash: &Ed25519KeyHash,
input: &TransactionInput,
amount: &Value,
) {
let inp = TxBuilderInput {
input: input.clone(),
amount: amount.clone(),
};
self.push_input((inp, None));
self.required_witnesses.vkeys.add_move(hash.clone());
}
fn add_script_input(&mut self, hash: &ScriptHash, input: &TransactionInput, amount: &Value) {
let inp = TxBuilderInput {
input: input.clone(),
amount: amount.clone(),
};
self.push_input((inp, Some(hash.clone())));
self.insert_input_with_empty_witness(hash, input);
}
pub fn add_native_script_input(
&mut self,
script: &NativeScriptSource,
input: &TransactionInput,
amount: &Value,
) {
let hash = script.script_hash();
self.add_script_input(&hash, input, amount);
let witness = ScriptWitnessType::NativeScriptWitness(script.0.clone());
self.insert_input_with_witness(&hash, input, &witness);
}
pub fn add_plutus_script_input(
&mut self,
witness: &PlutusWitness,
input: &TransactionInput,
amount: &Value,
) {
let hash = witness.script.script_hash();
self.add_script_input(&hash, input, amount);
let witness = ScriptWitnessType::PlutusScriptWitness(witness.clone());
self.insert_input_with_witness(&hash, input, &witness);
}
pub fn add_bootstrap_input(
&mut self,
address: &ByronAddress,
input: &TransactionInput,
amount: &Value,
) {
let inp = TxBuilderInput {
input: input.clone(),
amount: amount.clone(),
};
self.push_input((inp, None));
self.required_witnesses.bootstraps.insert(address.to_bytes());
}
pub fn add_regular_input(
&mut self,
address: &Address,
input: &TransactionInput,
amount: &Value,
) -> Result<(), JsError> {
match &address.0 {
AddrType::Base(base_addr) => match &base_addr.payment.0 {
CredType::Key(key) => {
self.add_key_input(key, input, amount);
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Enterprise(ent_aaddr) => match &ent_aaddr.payment.0 {
CredType::Key(key) => {
self.add_key_input(key, input, amount);
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Ptr(ptr_addr) => match &ptr_addr.payment.0 {
CredType::Key(key) => {
self.add_key_input(key, input, amount);
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Byron(byron_addr) => {
self.add_bootstrap_input(byron_addr, input, amount);
Ok(())
}
AddrType::Reward(_) => Err(JsError::from_str(
&BuilderError::RegularInputIsFromRewardAddress.as_str(),
)),
AddrType::Malformed(_) => {
Err(JsError::from_str(&BuilderError::MalformedAddress.as_str()))
}
}
}
pub fn get_ref_inputs(&self) -> TransactionInputs {
let mut inputs = Vec::new();
for wintess in self
.required_witnesses
.scripts
.iter()
.flat_map(|(_, tx_wits)| tx_wits.values())
.filter_map(|wit| wit.as_ref())
{
match wintess {
ScriptWitnessType::NativeScriptWitness(NativeScriptSourceEnum::RefInput(
input, _, _, _,
)) => {
inputs.push(input.clone());
}
ScriptWitnessType::PlutusScriptWitness(plutus_witness) => {
if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum {
inputs.push(input.clone());
}
if let PlutusScriptSourceEnum::RefInput(script_ref, _) = &plutus_witness.script
{
inputs.push(script_ref.input_ref.clone());
}
}
_ => (),
}
}
TransactionInputs::from_vec(inputs)
}
pub fn get_native_input_scripts(&self) -> Option<NativeScripts> {
let mut scripts = NativeScripts::new();
self.required_witnesses
.scripts
.values()
.flat_map(|v| v)
.for_each(|tx_in_with_wit| {
if let Some(ScriptWitnessType::NativeScriptWitness(
NativeScriptSourceEnum::NativeScript(s, _),
)) = tx_in_with_wit.1
{
scripts.add(&s);
}
});
if scripts.len() > 0 {
Some(scripts)
} else {
None
}
}
pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet<Language> {
let mut used_langs = BTreeSet::new();
for input_with_wit in self.required_witnesses.scripts.values() {
for (_, script_wit) in input_with_wit {
if let Some(ScriptWitnessType::PlutusScriptWitness(plutus_witness)) = script_wit {
used_langs.insert(plutus_witness.script.language());
}
}
}
used_langs
}
pub fn get_plutus_input_scripts(&self) -> Option<PlutusWitnesses> {
let tag = RedeemerTag::new_spend();
let script_hash_index_map: BTreeMap<&TransactionInput, BigNum> = self
.inputs
.values()
.enumerate()
.fold(BTreeMap::new(), |mut m, (i, (tx_in, hash_option))| {
if hash_option.is_some() {
m.insert(&tx_in.input, (i as u64).into());
}
m
});
let mut scripts = PlutusWitnesses::new();
self.required_witnesses
.scripts
.iter()
.flat_map(|x| x.1)
.for_each(|(hash, option)| {
if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = option {
if let Some(idx) = script_hash_index_map.get(&hash) {
scripts.add(&s.clone_with_redeemer_index_and_tag(&idx, &tag));
}
}
});
if scripts.len() > 0 {
Some(scripts)
} else {
None
}
}
pub(crate) fn has_plutus_scripts(&self) -> bool {
self.required_witnesses.scripts.values().any(|x| {
x.iter()
.any(|(_, w)| matches!(w, Some(ScriptWitnessType::PlutusScriptWitness(_))))
})
}
pub(crate) fn iter(&self) -> impl std::iter::Iterator<Item = &TxBuilderInput> + '_ {
self.inputs.values().map(|(i, _)| i)
}
pub fn len(&self) -> usize {
self.inputs.len()
}
pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) {
self.required_witnesses.vkeys.add_move(key.clone());
}
pub fn add_required_signers(&mut self, keys: &RequiredSigners) {
self.required_witnesses.vkeys.extend(keys);
}
pub fn total_value(&self) -> Result<Value, JsError> {
let mut res = Value::zero();
for (inp, _) in self.inputs.values() {
res = res.checked_add(&inp.amount)?;
}
Ok(res)
}
pub fn inputs(&self) -> TransactionInputs {
TransactionInputs::from_vec(
self.inputs
.values()
.map(|(ref tx_builder_input, _)| tx_builder_input.input.clone())
.collect(),
)
}
pub fn inputs_option(&self) -> Option<TransactionInputs> {
if self.len() > 0 {
Some(self.inputs())
} else {
None
}
}
pub(crate) fn get_script_ref_inputs_with_size(
&self,
) -> impl Iterator<Item = (&TransactionInput, usize)> {
self.required_witnesses
.scripts
.iter()
.flat_map(|(_, tx_wits)| tx_wits.iter())
.filter_map(|(_, wit)| wit.as_ref())
.filter_map(|wit| wit.get_script_ref_input_with_size())
}
#[allow(dead_code)]
pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes {
self.into()
}
pub(crate) fn has_inputs(&self) -> bool {
!self.inputs.is_empty()
}
pub(crate) fn has_input(&self, input: &TransactionInput) -> bool {
self.inputs.contains_key(input)
}
fn insert_input_with_witness(
&mut self,
script_hash: &ScriptHash,
input: &TransactionInput,
witness: &ScriptWitnessType,
) {
let script_inputs = self
.required_witnesses
.scripts
.entry(script_hash.clone())
.or_insert(LinkedHashMap::new());
script_inputs.insert(input.clone(), Some(witness.clone()));
}
fn insert_input_with_empty_witness(
&mut self,
script_hash: &ScriptHash,
input: &TransactionInput,
) {
let script_inputs = self
.required_witnesses
.scripts
.entry(script_hash.clone())
.or_insert(LinkedHashMap::new());
script_inputs.insert(input.clone(), None);
}
}
impl From<&TxInputsBuilder> for Ed25519KeyHashes {
fn from(inputs: &TxInputsBuilder) -> Self {
let mut set = inputs.required_witnesses.vkeys.clone();
inputs
.required_witnesses
.scripts
.values()
.flat_map(|tx_wits| tx_wits.values())
.for_each(|swt: &Option<ScriptWitnessType>| {
match swt {
Some(ScriptWitnessType::NativeScriptWitness(script_source)) => {
if let Some(signers) = script_source.required_signers() {
set.extend_move(signers);
}
}
Some(ScriptWitnessType::PlutusScriptWitness(script_source)) => {
if let Some(signers) = script_source.get_required_signers() {
set.extend_move(signers);
}
}
None => (),
}
});
set
}
}