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
use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::Transaction;
use c_fixed_string::CFixedStr;
use cashaccount_sys::{
    cashacc_account_destroy, cashacc_account_init, cashacc_parse_opreturn,
    CASHACC_ERR_MALLOC_FAILED,
};
use std::ffi::CStr;

pub fn get_cashaccount_name(tx: &Transaction) -> Option<String> {
    let mut opreturn_found = false;
    let mut cashaccount_found = false;

    let account = unsafe { cashacc_account_init() };
    let _clean = scopeguard::guard((), |_| {
        unsafe { cashacc_account_destroy(account) };
    });

    for out in tx.output.iter() {
        if !out.script_pubkey.is_op_return() {
            continue;
        }

        if opreturn_found {
            // CashAccount transaction can only contain 1 OP_RETURN output.
            // We've now seen a second one.
            return None;
        }

        // OP_RETURN found. Parse to see if it contains a cashaccount.
        opreturn_found = true;
        let script: &Script = &out.script_pubkey;
        let bytes = CFixedStr::from_bytes(script.as_bytes());
        let rc =
            unsafe { cashacc_parse_opreturn(bytes.as_ptr(), script.len() as u32, false, account) };

        assert!(rc != CASHACC_ERR_MALLOC_FAILED);
        if rc < 1 {
            // not valid cashaccount, or no payload found.
            return None;
        }
        cashaccount_found = true;
    }
    if !cashaccount_found {
        return None;
    }
    let name = unsafe { CStr::from_ptr((*account).name).to_str().unwrap() };
    Some(name.to_string())
}