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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// SPDX-License-Identifier: CC0-1.0

use std::os::raw::{c_uchar, c_uint};

use crate::ffi::sha256::CSha256Midstate;
use hashes::{sha256, Hash};
use libc::size_t;

/// Documentation of CRawInputData/CRawOutputData/CRawTapData/CRaw
/// Data structure for holding data that CTransaction points to.
///
/// Why do we this special data structure?
///     1. We need to keep the data in memory until the CTransaction is dropped.
///     2. The memory is Transaction is not saved in the same format as required by FFI.
/// We use more ergonomics in rust to allow better UX which interfere with the FFI. For example,
/// the Value is stored as Tagged Union, but we require it to be a slice of bytes in elements format.
///     3. Allocating inside FFI functions does not work because the memory is freed after the function returns.
///     4. We only create allocations for data fields that are stored differently from the
/// consensus serialization format.
#[derive(Debug)]
pub struct RawOutputData {
    pub asset: Vec<c_uchar>,
    pub value: Vec<c_uchar>,
    pub nonce: Vec<c_uchar>,
    pub surjection_proof: Vec<c_uchar>,
    pub range_proof: Vec<c_uchar>,
}

#[derive(Debug)]
#[repr(C)]
pub struct CRawBuffer {
    pub ptr: *const c_uchar,
    pub len: u32,
}

/// Similar to [`CRawOutputData`], for inputs.
#[derive(Debug)]
pub struct RawInputData {
    pub annex: Option<Vec<c_uchar>>,
    // issuance
    pub issuance_amount: Vec<c_uchar>,
    pub issuance_inflation_keys: Vec<c_uchar>,
    pub amount_range_proof: Vec<c_uchar>,
    pub inflation_keys_range_proof: Vec<c_uchar>,
    // spent txo
    pub asset: Vec<c_uchar>,
    pub value: Vec<c_uchar>,
}

/// Similar to [`CRawOutputData`], but for transaction
#[derive(Debug)]
pub struct RawTransactionData {
    pub inputs: Vec<RawInputData>,
    pub outputs: Vec<RawOutputData>,
}

#[derive(Debug)]
#[repr(C)]
pub struct CRawOutput {
    asset: *const c_uchar,
    value: *const c_uchar,
    nonce: *const c_uchar,
    script_pubkey: CRawBuffer,
    surjection_proof: CRawBuffer,
    range_proof: CRawBuffer,
}

#[derive(Debug)]
#[repr(C)]
pub struct CRawInput {
    annex: *const CRawBuffer,
    prev_txid: *const c_uchar,
    pegin: *const c_uchar,
    // issuance
    blinding_nonce: *const c_uchar,
    asset_entropy: *const c_uchar,
    amount: *const c_uchar,
    inflation_keys: *const c_uchar,
    amount_range_proof: CRawBuffer,
    inflation_keys_range_proof: CRawBuffer,
    // spent txo
    asset: *const c_uchar,
    value: *const c_uchar,
    script_pubkey: CRawBuffer,
    // inputs
    script_sig: CRawBuffer,
    prev_txout_index: u32,
    sequence: u32,
}

#[derive(Debug)]
#[repr(C)]
pub struct CRawTransaction {
    inputs: *const CRawInput,
    outputs: *const CRawOutput,
    version: u32,
    locktime: u32,
    n_inputs: u32,
    n_outputs: u32,
}

#[derive(Debug)]
#[repr(C)]
pub struct CRawTapEnv {
    control_block: *const c_uchar,
    script_cmr: *const c_uchar,
    branch_len: u8,
}

#[derive(Debug)]
pub enum CTransaction {}

#[derive(Debug)]
#[repr(C)]
pub struct CElementsTxEnv {
    tx: *const CTransaction,
    taproot: *const CTapEnv,
    genesis_hash: CSha256Midstate,
    sighash_all: CSha256Midstate,
    ix: usize, // This is uint_fast32_t in C, which is usize for 32/64 bits
}

#[derive(Debug)]
pub enum CTapEnv {}

extern "C" {

    pub static c_sizeof_rawBuffer: size_t;
    pub static c_sizeof_rawOutput: size_t;
    pub static c_sizeof_rawInput: size_t;
    pub static c_sizeof_rawTransaction: size_t;
    pub static c_sizeof_rawTapEnv: size_t;
    pub static c_sizeof_txEnv: size_t;

    pub static c_alignof_rawBuffer: size_t;
    pub static c_alignof_rawOutput: size_t;
    pub static c_alignof_rawInput: size_t;
    pub static c_alignof_rawTransaction: size_t;
    pub static c_alignof_rawTapEnv: size_t;
    pub static c_alignof_txEnv: size_t;

    pub fn c_set_rawBuffer(res: *mut CRawBuffer, buf: *const c_uchar, len: c_uint);
    pub fn c_set_rawOutput(
        res: *mut CRawOutput,
        asset: *const c_uchar,
        value: *const c_uchar,
        nonce: *const c_uchar,
        scriptPubKey: *const CRawBuffer,
        surjectionProof: *const CRawBuffer,
        rangeProof: *const CRawBuffer,
    );
    pub fn c_set_rawInput(
        result: *mut CRawInput,
        annex: *const CRawBuffer,
        pegin: *const c_uchar,
        scriptSig: *const CRawBuffer,
        prevTxid: *const c_uchar,
        prevIx: c_uint,
        asset: *const c_uchar,
        value: *const c_uchar,
        scriptPubKey: *const CRawBuffer,
        sequence: c_uint,
        blindingNonce: *const c_uchar,
        assetEntropy: *const c_uchar,
        amount: *const c_uchar,
        inflationKeys: *const c_uchar,
        amountRangePrf: *const CRawBuffer,
        inflationKeysRangePrf: *const CRawBuffer,
    );

    pub fn c_set_rawTransaction(
        result: *mut CRawTransaction,
        version: c_uint,
        input: *const CRawInput,
        numInputs: c_uint,
        output: *const CRawOutput,
        numOutputs: c_uint,
        lockTime: c_uint,
    );
    pub fn c_set_rawTapEnv(
        result: *mut CRawTapEnv,
        controlBlock: *const c_uchar,
        pathLen: c_uchar,
        scriptCMR: *const c_uchar,
    );
    pub fn c_set_txEnv(
        result: *mut CElementsTxEnv,
        tx: *const CTransaction,
        taproot: *const CTapEnv,
        genesisHash: *const c_uchar,
        ix: c_uint,
    );
    pub fn elements_simplicity_mallocTapEnv(rawEnv: *const CRawTapEnv) -> *mut CTapEnv;
    pub fn elements_simplicity_mallocTransaction(
        rawTx: *const CRawTransaction,
    ) -> *mut CTransaction;
    pub fn c_free_transaction(tx: *mut CTransaction);
    pub fn c_free_tapEnv(env: *mut CTapEnv);
}
impl CElementsTxEnv {
    pub fn sighash_all(&self) -> sha256::Hash {
        let midstate: sha256::Midstate = self.sighash_all.into();
        sha256::Hash::from_byte_array(midstate.to_byte_array())
    }
}

// Pointer must be manually free after dropping
impl Drop for CElementsTxEnv {
    fn drop(&mut self) {
        unsafe {
            c_free_transaction(self.tx as *mut CTransaction);
            c_free_tapEnv(self.taproot as *mut CTapEnv);
        }
    }
}

impl CRawBuffer {
    pub fn new(buf: &[c_uchar]) -> Self {
        unsafe {
            let mut raw_buffer = std::mem::MaybeUninit::<CRawBuffer>::uninit();
            c_set_rawBuffer(raw_buffer.as_mut_ptr(), buf.as_ptr(), buf.len() as c_uint);
            raw_buffer.assume_init()
        }
    }
}

#[cfg(test)]
mod tests {
    use std::mem::{align_of, size_of};

    use crate::c_jets::{c_env::*, frame_ffi::*};
    use crate::ffi::*;

    #[test]
    fn test_sizes() {
        unsafe {
            assert_eq!(size_of::<u32>(), c_sizeof_ubounded);
            assert_eq!(size_of::<usize>(), c_sizeof_UWORD);
            assert_eq!(size_of::<CFrameItem>(), c_sizeof_frameItem);
            assert_eq!(size_of::<CRawBuffer>(), c_sizeof_rawBuffer);
            assert_eq!(size_of::<CRawInput>(), c_sizeof_rawInput);
            assert_eq!(size_of::<CRawOutput>(), c_sizeof_rawOutput);
            assert_eq!(size_of::<CRawTransaction>(), c_sizeof_rawTransaction);
            assert_eq!(size_of::<CRawTapEnv>(), c_sizeof_rawTapEnv);
            assert_eq!(size_of::<CElementsTxEnv>(), c_sizeof_txEnv);
        }
    }

    #[test]
    fn test_aligns() {
        unsafe {
            assert_eq!(align_of::<u32>(), c_alignof_ubounded);
            assert_eq!(align_of::<usize>(), c_alignof_UWORD);
            assert_eq!(align_of::<CFrameItem>(), c_alignof_frameItem);
            assert_eq!(align_of::<CRawBuffer>(), c_alignof_rawBuffer);
            assert_eq!(align_of::<CRawInput>(), c_alignof_rawInput);
            assert_eq!(align_of::<CRawOutput>(), c_alignof_rawOutput);
            assert_eq!(align_of::<CRawTransaction>(), c_alignof_rawTransaction);
            assert_eq!(align_of::<CRawTapEnv>(), c_alignof_rawTapEnv);
            assert_eq!(align_of::<CElementsTxEnv>(), c_alignof_txEnv);
        }
    }
}