use std::error;
use std::fmt::Debug;
use std::io;
use thiserror::Error;
use crate::blockchain::Network;
use crate::consensus::{self, Decodable, Encodable};
use crate::script::{DataLock, DataPunishableLock, ScriptPath};
#[derive(Error, Debug)]
pub enum Error {
#[error("Missing UTXO")]
MissingUTXO,
#[error("Missing signature")]
MissingSignature,
#[error("Missing witness data")]
MissingWitness,
#[error("Missing network data")]
MissingNetwork,
#[error("Public key not found in the partial transaction")]
MissingPublicKey,
#[error("The transaction has not been seen on-chain yet")]
MissingOnchainTransaction,
#[error("The targeted amount is invalid")]
InvalidTargetAmount,
#[error("Not enough assets to create the transaction")]
NotEnoughAssets,
#[error("Wrong transaction template: {0}")]
WrongTemplate(&'static str),
#[error("The transaction chain validation failed")]
InvalidTransactionChain,
#[error("Transaction error: {0}")]
Other(Box<dyn error::Error + Send + Sync>),
}
impl Error {
pub fn new<E>(error: E) -> Self
where
E: Into<Box<dyn error::Error + Send + Sync>>,
{
Self::Other(error.into())
}
pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
match self {
Self::Other(error) => Some(error),
_ => None,
}
}
}
pub trait Transaction<Px, Out, Amt> {
fn as_partial(&self) -> &Px;
fn as_partial_mut(&mut self) -> &mut Px;
fn to_partial(self) -> Px;
fn from_partial(partial: Px) -> Self;
fn based_on(&self) -> Out;
fn output_amount(&self) -> Amt;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Serialize, Deserialize)]
pub enum TxLabel {
Funding,
#[display("Arbitrating Lock")]
Lock,
Buy,
Cancel,
Refund,
Punish,
#[display("Accordant Lock")]
AccLock,
}
impl Encodable for TxLabel {
fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
match self {
TxLabel::Funding => 0x01u16.consensus_encode(writer),
TxLabel::Lock => 0x02u16.consensus_encode(writer),
TxLabel::Buy => 0x03u16.consensus_encode(writer),
TxLabel::Cancel => 0x04u16.consensus_encode(writer),
TxLabel::Refund => 0x05u16.consensus_encode(writer),
TxLabel::Punish => 0x06u16.consensus_encode(writer),
TxLabel::AccLock => 0x07u16.consensus_encode(writer),
}
}
}
impl Decodable for TxLabel {
fn consensus_decode<D: io::Read>(d: &mut D) -> Result<Self, consensus::Error> {
match Decodable::consensus_decode(d)? {
0x01u16 => Ok(TxLabel::Funding),
0x02u16 => Ok(TxLabel::Lock),
0x03u16 => Ok(TxLabel::Buy),
0x04u16 => Ok(TxLabel::Cancel),
0x05u16 => Ok(TxLabel::Refund),
0x06u16 => Ok(TxLabel::Punish),
0x07u16 => Ok(TxLabel::AccLock),
_ => Err(consensus::Error::UnknownType),
}
}
}
impl_strict_encoding!(TxLabel);
pub trait Witnessable<Ms, Pk, Si> {
fn generate_witness_message(&self, path: ScriptPath) -> Result<Ms, Error>;
fn add_witness(&mut self, pubkey: Pk, sig: Si) -> Result<(), Error>;
}
pub trait Finalizable {
fn finalize(&mut self) -> Result<(), Error>;
}
pub trait Broadcastable<Tx>: Finalizable {
fn extract(&self) -> Tx;
fn finalize_and_extract(&mut self) -> Result<Tx, Error> {
self.finalize()?;
Ok(self.extract())
}
}
pub trait Linkable<Out> {
fn get_consumable_output(&self) -> Result<Out, Error>;
}
pub trait Chainable<Px, Out, Amt>: Transaction<Px, Out, Amt>
where
Out: Eq,
{
fn is_build_on_top_of(&self, prev: &impl Linkable<Out>) -> Result<(), Error>;
}
impl<T, Px, Out, Amt> Chainable<Px, Out, Amt> for T
where
Out: Eq,
T: Transaction<Px, Out, Amt>,
{
fn is_build_on_top_of(&self, prev: &impl Linkable<Out>) -> Result<(), Error> {
match self.based_on() == prev.get_consumable_output()? {
true => Ok(()),
false => Err(Error::InvalidTransactionChain),
}
}
}
pub trait Fundable<Tx, Out, Addr, Pk>: Linkable<Out> {
fn initialize(pubkey: Pk, network: Network) -> Result<Self, Error>
where
Self: Sized;
fn get_address(&self) -> Result<Addr, Error>;
fn update(&mut self, tx: Tx) -> Result<(), Error>;
fn was_seen(&self) -> bool;
fn raw(tx: Tx) -> Result<Self, Error>
where
Self: Sized;
fn get_label(&self) -> TxLabel {
TxLabel::Funding
}
}
pub trait Lockable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>:
Transaction<Px, Out, Amt> + Broadcastable<Tx> + Linkable<Out> + Witnessable<Ms, Pk, Si>
{
fn initialize(
prev: &impl Fundable<Tx, Out, Addr, Pk>,
lock: DataLock<Ti, Pk>,
target_amount: Amt,
) -> Result<Self, Error>
where
Self: Sized;
fn verify_template(&self, lock: DataLock<Ti, Pk>) -> Result<(), Error>;
fn verify_target_amount(&self, target_amount: Amt) -> Result<(), Error>
where
Amt: PartialEq,
{
match self.output_amount() == target_amount {
true => Ok(()),
false => Err(Error::InvalidTargetAmount),
}
}
fn get_label(&self) -> TxLabel {
TxLabel::Lock
}
}
pub trait Buyable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>:
Transaction<Px, Out, Amt>
+ Broadcastable<Tx>
+ Linkable<Out>
+ Witnessable<Ms, Pk, Si>
+ Chainable<Px, Out, Amt>
where
Out: Eq,
{
fn initialize(
prev: &impl Lockable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>,
lock: DataLock<Ti, Pk>,
destination_target: Addr,
) -> Result<Self, Error>
where
Self: Sized;
fn verify_template(&self, destination_target: Addr) -> Result<(), Error>;
fn extract_witness(tx: Tx) -> Si;
fn get_label(&self) -> TxLabel {
TxLabel::Buy
}
}
pub trait Cancelable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>:
Transaction<Px, Out, Amt>
+ Broadcastable<Tx>
+ Linkable<Out>
+ Witnessable<Ms, Pk, Si>
+ Chainable<Px, Out, Amt>
where
Out: Eq,
{
fn initialize(
prev: &impl Lockable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>,
lock: DataLock<Ti, Pk>,
punish_lock: DataPunishableLock<Ti, Pk>,
) -> Result<Self, Error>
where
Self: Sized;
fn verify_template(
&self,
lock: DataLock<Ti, Pk>,
punish_lock: DataPunishableLock<Ti, Pk>,
) -> Result<(), Error>;
fn get_label(&self) -> TxLabel {
TxLabel::Cancel
}
}
pub trait Refundable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>:
Transaction<Px, Out, Amt>
+ Broadcastable<Tx>
+ Linkable<Out>
+ Witnessable<Ms, Pk, Si>
+ Chainable<Px, Out, Amt>
where
Out: Eq,
{
fn initialize(
prev: &impl Cancelable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>,
refund_target: Addr,
) -> Result<Self, Error>
where
Self: Sized;
fn verify_template(&self, refund_target: Addr) -> Result<(), Error>;
fn extract_witness(tx: Tx) -> Si;
fn get_label(&self) -> TxLabel {
TxLabel::Refund
}
}
pub trait Punishable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>:
Transaction<Px, Out, Amt>
+ Broadcastable<Tx>
+ Linkable<Out>
+ Witnessable<Ms, Pk, Si>
+ Chainable<Px, Out, Amt>
where
Out: Eq,
{
fn initialize(
prev: &impl Cancelable<Addr, Tx, Px, Out, Amt, Ti, Ms, Pk, Si>,
punish_lock: DataPunishableLock<Ti, Pk>,
destination_target: Addr,
) -> Result<Self, Error>
where
Self: Sized;
fn get_label(&self) -> TxLabel {
TxLabel::Punish
}
}