use amplify::Slice32;
use bitcoin::psbt::raw::ProprietaryKey;
use crate::Output;
pub const PSBT_OPRET_PREFIX: &[u8] = b"OPRET";
pub const PSBT_OUT_OPRET_HOST: u8 = 0x00;
pub const PSBT_OUT_OPRET_COMMITMENT: u8 = 0x01;
pub trait ProprietaryKeyOpret {
fn opret_host() -> ProprietaryKey {
ProprietaryKey {
prefix: PSBT_OPRET_PREFIX.to_vec(),
subtype: PSBT_OUT_OPRET_HOST,
key: vec![],
}
}
fn opret_commitment() -> ProprietaryKey {
ProprietaryKey {
prefix: PSBT_OPRET_PREFIX.to_vec(),
subtype: PSBT_OUT_OPRET_COMMITMENT,
key: vec![],
}
}
}
impl ProprietaryKeyOpret for ProprietaryKey {}
#[derive(
Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error
)]
#[display(doc_comments)]
pub enum OpretKeyError {
OutputAlreadyHasCommitment,
NonOpReturnOutput,
OpretProhibited,
}
impl Output {
#[inline]
pub fn is_opret_host(&self) -> bool {
self.proprietary.contains_key(&ProprietaryKey::opret_host()) && self.script.is_op_return()
}
#[inline]
pub fn set_opret_host(&mut self) -> Result<bool, OpretKeyError> {
if !self.script.is_op_return() {
return Err(OpretKeyError::NonOpReturnOutput);
}
Ok(self
.proprietary
.insert(ProprietaryKey::opret_host(), vec![])
.is_some())
}
pub fn has_opret_commitment(&self) -> Result<bool, OpretKeyError> {
if !self.script.is_op_return() {
return Err(OpretKeyError::NonOpReturnOutput);
}
Ok(self
.proprietary
.contains_key(&ProprietaryKey::opret_commitment()))
}
pub fn opret_commitment(&self) -> Result<Option<Slice32>, OpretKeyError> {
if !self.script.is_op_return() {
return Err(OpretKeyError::NonOpReturnOutput);
}
Ok(self
.proprietary
.get(&ProprietaryKey::opret_commitment())
.and_then(Slice32::from_slice))
}
pub fn set_opret_commitment(
&mut self,
commitment: impl Into<[u8; 32]>,
) -> Result<(), OpretKeyError> {
if !self.is_opret_host() {
return Err(OpretKeyError::OpretProhibited);
}
if self.has_opret_commitment()? {
return Err(OpretKeyError::OutputAlreadyHasCommitment);
}
self.proprietary.insert(
ProprietaryKey::opret_commitment(),
commitment.into().to_vec(),
);
Ok(())
}
}