use std::ops::Deref;
use std::sync::Arc;
use anyhow::ensure;
use crate::consensus_rule_set::ConsensusRuleSet;
use crate::proof_abstractions::tasm::program::TritonVmProofJobOptions;
use crate::proof_abstractions::triton_vm_job_queue::TritonVmJobQueue;
use crate::transaction::transaction_kernel::TransactionKernel;
use crate::transaction::transaction_proof::TransactionProof;
use crate::transaction::validity::tasm::single_proof::merge_branch::bound_time_diff::BoundTimeDiff;
use crate::transaction::validity::tasm::single_proof::merge_branch::MergeWitness;
use crate::transaction::Transaction;
#[derive(Debug, Clone)]
pub struct BlockTransactionKernel(TransactionKernel);
impl Deref for BlockTransactionKernel {
type Target = TransactionKernel;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<BlockTransactionKernel> for TransactionKernel {
fn from(value: BlockTransactionKernel) -> Self {
value.0
}
}
impl TryFrom<TransactionKernel> for BlockTransactionKernel {
type Error = ();
fn try_from(value: TransactionKernel) -> Result<Self, Self::Error> {
match value.merge_bit {
true => Ok(Self(value)),
false => Err(()),
}
}
}
#[derive(Debug, Clone)]
pub enum BlockOrRegularTransactionKernel {
Block(BlockTransactionKernel),
Regular(TransactionKernel),
}
impl From<BlockOrRegularTransactionKernel> for TransactionKernel {
fn from(value: BlockOrRegularTransactionKernel) -> Self {
match value {
BlockOrRegularTransactionKernel::Block(block_transaction_kernel) => {
block_transaction_kernel.0
}
BlockOrRegularTransactionKernel::Regular(transaction_kernel) => transaction_kernel,
}
}
}
impl Deref for BlockOrRegularTransactionKernel {
type Target = TransactionKernel;
fn deref(&self) -> &Self::Target {
match self {
Self::Block(kernel) => kernel,
Self::Regular(kernel) => kernel,
}
}
}
#[derive(Debug, Clone)]
pub struct BlockTransaction {
pub(crate) kernel: BlockTransactionKernel,
pub(crate) proof: TransactionProof,
}
impl TryFrom<Transaction> for BlockTransaction {
type Error = ();
fn try_from(value: Transaction) -> Result<Self, Self::Error> {
Ok(Self {
kernel: value.kernel.try_into()?,
proof: value.proof,
})
}
}
impl From<BlockTransaction> for Transaction {
fn from(value: BlockTransaction) -> Self {
Self {
kernel: value.kernel.0,
proof: value.proof,
}
}
}
#[derive(Debug, Clone)]
pub enum BlockOrRegularTransaction {
Block(BlockTransaction),
Regular(Transaction),
}
impl BlockOrRegularTransaction {
pub fn kernel(&self) -> BlockOrRegularTransactionKernel {
match self {
BlockOrRegularTransaction::Block(block_transaction) => {
BlockOrRegularTransactionKernel::Block(block_transaction.kernel.clone())
}
BlockOrRegularTransaction::Regular(transaction) => {
BlockOrRegularTransactionKernel::Regular(transaction.kernel.clone())
}
}
}
pub fn proof(&self) -> TransactionProof {
match self {
BlockOrRegularTransaction::Block(block_transaction) => block_transaction.proof.clone(),
BlockOrRegularTransaction::Regular(transaction) => transaction.proof.clone(),
}
}
}
impl From<Transaction> for BlockOrRegularTransaction {
fn from(value: Transaction) -> Self {
BlockOrRegularTransaction::Regular(value)
}
}
impl From<BlockTransaction> for BlockOrRegularTransaction {
fn from(value: BlockTransaction) -> Self {
BlockOrRegularTransaction::Block(value)
}
}
impl TryFrom<BlockOrRegularTransaction> for BlockTransaction {
type Error = ();
fn try_from(value: BlockOrRegularTransaction) -> Result<Self, Self::Error> {
match value {
BlockOrRegularTransaction::Block(block_transaction) => Ok(block_transaction),
BlockOrRegularTransaction::Regular(_) => Err(()),
}
}
}
impl From<BlockOrRegularTransaction> for Transaction {
fn from(value: BlockOrRegularTransaction) -> Self {
match value {
BlockOrRegularTransaction::Block(block_transaction) => Self {
kernel: block_transaction.kernel.into(),
proof: block_transaction.proof,
},
BlockOrRegularTransaction::Regular(transaction) => transaction,
}
}
}
impl BlockTransaction {
pub fn new(kernel: BlockTransactionKernel, proof: TransactionProof) -> Self {
Self { kernel, proof }
}
pub fn kernel(&self) -> &TransactionKernel {
&self.kernel
}
pub fn proof(&self) -> &TransactionProof {
&self.proof
}
pub async fn merge(
coinbase: BlockOrRegularTransaction,
other: Transaction,
shuffle_seed: [u8; 32],
triton_vm_job_queue: Arc<TritonVmJobQueue>,
proof_job_options: TritonVmProofJobOptions,
#[expect(unused_variables, reason = "anticipate future fork")]
consensus_rule_set: ConsensusRuleSet,
) -> anyhow::Result<BlockTransaction> {
let cb_ts = coinbase.kernel().timestamp;
let other_ts = other.kernel.timestamp;
let diff = std::cmp::max(cb_ts, other_ts) - std::cmp::min(cb_ts, other_ts);
ensure!(
diff <= BoundTimeDiff::MAX_TIMESTAMP_DIFF,
"Time difference may not be too big when merging with coinbase transactions"
);
let merge_witness = MergeWitness::for_composition(coinbase, other, shuffle_seed);
let tx = MergeWitness::merge(merge_witness, triton_vm_job_queue, proof_job_options).await?;
Ok(tx.try_into().expect("Must have merge bit set"))
}
}
#[cfg(any(test, feature = "test-helpers"))]
impl BlockTransaction {
pub fn upgrade(tx: Transaction) -> Self {
use neptune_mutator_set::removal_record::removal_record_list::RemovalRecordList;
use crate::transaction::transaction_kernel::TransactionKernelModifier;
let packed = RemovalRecordList::pack(tx.kernel.inputs.clone());
let kernel = TransactionKernelModifier::default()
.merge_bit(true)
.inputs(packed)
.modify(tx.kernel);
let transaction = Transaction {
kernel,
proof: tx.proof,
};
Self::try_from(transaction).expect("just set merge bit")
}
pub fn from_tx_kernel(kernel: TransactionKernel) -> Self {
use neptune_mutator_set::removal_record::removal_record_list::RemovalRecordList;
use crate::transaction::transaction_kernel::TransactionKernelModifier;
let packed = RemovalRecordList::pack(kernel.inputs.clone());
let kernel = TransactionKernelModifier::default()
.merge_bit(true)
.inputs(packed)
.modify(kernel);
let transaction = Transaction {
kernel,
proof: TransactionProof::invalid(),
};
Self::try_from(transaction).expect("just set merge bit")
}
}