1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::{Error, ACCEPTED_MAX_TRANSACTION_OUTPUTS};
use bitcoin::types::{BlockHeader, H256Le, Transaction, Value};
pub use bitcoin::Address as BtcAddress;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{dispatch::DispatchError, ensure};
use scale_info::TypeInfo;
use sp_core::H256;
use sp_std::{convert::TryFrom, vec::Vec};
#[derive(Encode, Decode, Default, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
pub struct RichBlockHeader<BlockNumber> {
pub block_header: BlockHeader,
pub block_height: u32,
pub chain_id: u32,
pub para_height: BlockNumber,
}
impl<BlockNumber> RichBlockHeader<BlockNumber> {
pub fn new(block_header: BlockHeader, chain_id: u32, block_height: u32, para_height: BlockNumber) -> Self {
RichBlockHeader {
block_header,
block_height,
chain_id,
para_height,
}
}
pub fn block_hash(&self) -> H256Le {
self.block_header.hash
}
}
#[cfg_attr(feature = "std", derive(Debug, PartialEq))]
pub struct OpReturnPaymentData<T: frame_system::Config> {
pub op_return: H256,
payments: Vec<(Value, BtcAddress)>,
_marker: sp_std::marker::PhantomData<T>,
}
impl<T: crate::Config> TryFrom<Transaction> for OpReturnPaymentData<T> {
type Error = DispatchError;
fn try_from(transaction: Transaction) -> Result<Self, Self::Error> {
ensure!(
transaction.outputs.len() <= ACCEPTED_MAX_TRANSACTION_OUTPUTS,
Error::<T>::InvalidOpReturnTransaction
);
let mut payments = Vec::new();
let mut op_returns = Vec::new();
for tx in transaction.outputs {
if let Ok(address) = tx.extract_address() {
payments.push((tx.value, address));
} else if let Ok(data) = tx.script.extract_op_return_data() {
ensure!(tx.value == 0, Error::<T>::InvalidOpReturnTransaction);
ensure!(data.len() == 32, Error::<T>::InvalidOpReturnTransaction);
op_returns.push(H256::from_slice(&data));
} else {
return Err(Error::<T>::InvalidOpReturnTransaction.into());
}
}
ensure!(op_returns.len() == 1, Error::<T>::InvalidOpReturnTransaction);
match payments.len() {
1 => (),
2 => {
ensure!(payments[0].1 != payments[1].1, Error::<T>::InvalidOpReturnTransaction);
}
_ => return Err(Error::<T>::InvalidOpReturnTransaction.into()),
}
Ok(Self {
op_return: op_returns.remove(0),
payments,
_marker: Default::default(),
})
}
}
impl<T: crate::Config> OpReturnPaymentData<T> {
pub fn ensure_valid_payment_to(
&self,
expected_amount: Value,
recipient: BtcAddress,
op_return: Option<H256>,
) -> Result<Option<BtcAddress>, DispatchError> {
if let Some(op_return) = op_return {
ensure!(op_return == self.op_return, Error::<T>::InvalidPayment);
}
let paid_amount = self
.payments
.iter()
.find_map(|&(amount, address)| if address == recipient { Some(amount) } else { None })
.ok_or(Error::<T>::InvalidPayment)?;
ensure!(paid_amount == expected_amount, Error::<T>::InvalidPaymentAmount);
Ok(self
.payments
.iter()
.find_map(|&(_, address)| if address != recipient { Some(address) } else { None }))
}
}