use crate::util::create_default_tx_builder;
use crate::{CardanoAddress, CardanoFormat, CardanoPublicKey};
use anychain_core::{Transaction, TransactionError, TransactionId};
use cml_chain::Deserialize;
use cml_chain::{
assets::Value,
builders::{
input_builder::SingleInputBuilder, output_builder::TransactionOutputBuilder,
tx_builder::ChangeSelectionAlgo,
},
crypto::{Vkey, Vkeywitness},
transaction::{
Transaction as SignedTransaction, TransactionInput, TransactionOutput,
TransactionWitnessSet,
},
utils::NetworkId,
};
use cml_core::serialization::{RawBytesEncoding, Serialize};
use cml_crypto::{blake2b256, Ed25519Signature, TransactionHash};
use std::{fmt, str::FromStr};
#[derive(Debug, Clone)]
pub struct Input {
pub txid: String,
pub index: u64,
pub address: Option<CardanoAddress>,
pub amount: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct Output {
pub address: CardanoAddress,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct CardanoTransactionParameters {
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
pub slot: u64,
pub network: u8,
}
#[derive(Debug, Clone)]
pub struct CardanoSignature {
pub public_key: Vec<u8>,
pub rs: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CardanoTransaction {
pub params: CardanoTransactionParameters,
pub signatures: Option<Vec<CardanoSignature>>,
}
impl FromStr for CardanoTransaction {
type Err = TransactionError;
fn from_str(_: &str) -> Result<Self, Self::Err> {
todo!()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CardanoTransactionId(pub [u8; 32]);
impl fmt::Display for CardanoTransactionId {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
todo!()
}
}
impl TransactionId for CardanoTransactionId {}
impl CardanoTransaction {
pub fn sign(&mut self, sigs: Vec<CardanoSignature>) -> Result<Vec<u8>, TransactionError> {
self.signatures = Some(sigs);
self.to_bytes()
}
}
impl Transaction for CardanoTransaction {
type Address = CardanoAddress;
type Format = CardanoFormat;
type PublicKey = CardanoPublicKey;
type TransactionId = CardanoTransactionId;
type TransactionParameters = CardanoTransactionParameters;
fn new(params: &Self::TransactionParameters) -> Result<Self, TransactionError> {
Ok(CardanoTransaction {
params: params.clone(),
signatures: None,
})
}
fn sign(&mut self, _: Vec<u8>, _: u8) -> Result<Vec<u8>, TransactionError> {
todo!()
}
fn to_bytes(&self) -> Result<Vec<u8>, TransactionError> {
let mut builder = create_default_tx_builder();
let change_address = self.params.inputs[0].address.clone();
let change_address = &change_address.unwrap().0;
for input in &self.params.inputs {
let txid = TransactionHash::from_hex(&input.txid)
.map_err(|e| TransactionError::Message(e.to_string()))?;
let address = input.address.clone().unwrap().0.clone();
let amount = Value::from(input.amount.unwrap());
let input = TransactionInput::new(txid, input.index);
let output = TransactionOutput::new(address, amount, None, None);
let input = SingleInputBuilder::new(input, output)
.payment_key()
.map_err(|e| TransactionError::Message(e.to_string()))?;
builder
.add_input(input)
.map_err(|e| TransactionError::Message(e.to_string()))?;
}
for output in &self.params.outputs {
let output = TransactionOutputBuilder::new()
.with_address(output.address.0.clone())
.next()
.map_err(|e| TransactionError::Message(e.to_string()))?
.with_value(output.amount)
.build()
.map_err(|e| TransactionError::Message(e.to_string()))?;
builder
.add_output(output)
.map_err(|e| TransactionError::Message(e.to_string()))?;
}
let input_amount = builder
.get_explicit_input()
.map_err(|e| TransactionError::Message(e.to_string()))?;
let output_amount = builder
.get_explicit_output()
.map_err(|e| TransactionError::Message(e.to_string()))?;
let fee = input_amount.coin - output_amount.coin;
builder.set_fee(fee);
let network = NetworkId::from(self.params.network as u64);
builder.set_network_id(network);
builder.set_ttl(self.params.slot + 200);
let tx = builder
.build(ChangeSelectionAlgo::Default, change_address)
.map_err(|e| TransactionError::Message(e.to_string()))?
.body();
match &self.signatures {
Some(sigs) => {
let mut witnesses = vec![];
for sig in sigs {
let pk = sig.public_key.as_slice();
let rs = sig.rs.as_slice();
let pk = Vkey::from_raw_bytes(pk)
.map_err(|e| TransactionError::Message(e.to_string()))?;
let rs = Ed25519Signature::from_raw_bytes(rs)
.map_err(|e| TransactionError::Message(e.to_string()))?;
let witness = Vkeywitness::new(pk, rs);
witnesses.push(witness);
}
let mut witness_set = TransactionWitnessSet::new();
witness_set.vkeywitnesses = Some(witnesses.into());
let signed_tx = SignedTransaction::new(tx, witness_set, true, None);
Ok(signed_tx.to_cbor_bytes())
}
None => Ok(tx.to_cbor_bytes()),
}
}
fn from_bytes(stream: &[u8]) -> Result<Self, TransactionError> {
let signed_tx = SignedTransaction::from_cbor_bytes(stream)
.map_err(|e| TransactionError::Message(e.to_string()))?;
let tx = signed_tx.body;
let mut inputs = vec![];
let mut outputs = vec![];
for input in tx.inputs {
let txid = input.transaction_id.to_hex();
let index = input.index;
inputs.push(Input {
txid,
index,
address: None,
amount: None,
});
}
for output in tx.outputs {
let address = CardanoAddress(output.address().clone());
let amount = output.amount().coin;
outputs.push(Output { address, amount });
}
let network_id = tx.network_id.unwrap().network as u8;
Self::new(&CardanoTransactionParameters {
inputs,
outputs,
slot: 0,
network: network_id,
})
}
fn to_transaction_id(&self) -> Result<Self::TransactionId, TransactionError> {
let hash = blake2b256(&self.to_bytes()?);
Ok(CardanoTransactionId(hash))
}
}