Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet_ffi): new ffi method to create output features (fixed flag size for dibbler) #4118

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
242 changes: 241 additions & 1 deletion base_layer/wallet_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ extern crate lazy_static;
use core::ptr;
use std::{
boxed::Box,
convert::TryFrom,
ffi::{CStr, CString},
num::NonZeroU16,
path::PathBuf,
Expand Down Expand Up @@ -113,7 +114,18 @@ use tari_comms::{
types::{CommsPublicKey, CommsSecretKey},
};
use tari_comms_dht::{store_forward::SafConfig, DbConnectionUrl, DhtConfig};
use tari_core::transactions::{tari_amount::MicroTari, CryptoFactories};
use tari_core::transactions::{
tari_amount::MicroTari,
transaction_components::{
AssetOutputFeatures,
CommitteeDefinitionFeatures,
MintNonFungibleFeatures,
OutputFeaturesVersion,
OutputFlags,
SideChainCheckpointFeatures,
},
CryptoFactories,
};
use tari_crypto::{
keys::{PublicKey as PublicKeyTrait, SecretKey},
tari_utilities::ByteArray,
Expand Down Expand Up @@ -979,6 +991,138 @@ pub unsafe extern "C" fn commitment_signature_destroy(com_sig: *mut TariCommitme

/// -------------------------------------------------------------------------------------------- ///

/// ---------------------------------- Output Features ------------------------------------------///

/// Creates a TariOutputFeatures from byte values
///
/// ## Arguments
/// `version` - The encoded value of the version as a byte
/// `flags` - The encoded value of the flags as a byte
/// `maturity` - The encoded value maturity as bytes
/// `recovery_byte` - The encoded value of the recovery byte as a byte
/// `metadata` - The metadata componenet as a ByteVector. It cannot be null
/// `unique_id` - The unique id componenet as a ByteVector. It can be null
/// `mparent_public_key` - The parent public key component as a ByteVector. It can be null
/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions
/// as an out parameter.
///
/// ## Returns
/// `TariOutputFeatures` - Returns an output features object. Note that it will be ptr::null_mut() if any mandatory
/// arguments are null or if there was an error with the contents of bytes
///
/// # Safety
/// The ```output_features_destroy``` function must be called when finished with a TariOutputFeatures to
/// prevent a memory leak
#[no_mangle]
pub unsafe extern "C" fn output_features_create_from_bytes(
version: c_uchar,
flags: c_uchar,
maturity: c_ulonglong,
recovery_byte: c_uchar,
metadata: *const ByteVector,
unique_id: *const ByteVector,
parent_public_key: *const ByteVector,
error_out: *mut c_int,
) -> *mut TariOutputFeatures {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);
if metadata.is_null() {
error = LibWalletError::from(InterfaceError::NullError("metadata".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
}

let decoded_version = match OutputFeaturesVersion::try_from(version) {
Ok(v) => v,
Err(message) => {
error!(
target: LOG_TARGET,
"Error creating a OutputFeaturesVersion: {:?}", message
);
error = LibWalletError::from(InterfaceError::InvalidArgument("version".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
},
};

let decoded_flags = match OutputFlags::from_bits(flags) {
Some(flags_value) => flags_value,
None => {
error!(
target: LOG_TARGET,
"Error creating a OutputFlags from bytes: {:?}", flags
);
error = LibWalletError::from(InterfaceError::InvalidArgument("flags".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
},
};

let decoded_metadata = (*metadata).0.clone();

let mut decoded_unique_id = None;
if !unique_id.is_null() {
decoded_unique_id = Some((*unique_id).0.clone());
}

let mut decoded_parent_public_key: Option<PublicKey> = None;
if !parent_public_key.is_null() {
decoded_parent_public_key = match TariPublicKey::from_bytes(&(*parent_public_key).0.clone()) {
Ok(k) => Some(k),
Err(e) => {
error!(
target: LOG_TARGET,
"Error creating a Private Key (u) from bytes: {:?}", e
);
error = LibWalletError::from(e).code;
ptr::swap(error_out, &mut error as *mut c_int);
return ptr::null_mut();
},
};
}

// DAN layer features are still a work in progress
// so, for now, we do not expose any of those fields
let asset: Option<AssetOutputFeatures> = None;
let mint_non_fungible: Option<MintNonFungibleFeatures> = None;
let sidechain_checkpoint: Option<SideChainCheckpointFeatures> = None;
let committee_definition: Option<CommitteeDefinitionFeatures> = None;

let output_features = TariOutputFeatures::new(
decoded_version,
decoded_flags,
maturity,
recovery_byte,
decoded_metadata,
decoded_unique_id,
decoded_parent_public_key,
asset,
mint_non_fungible,
sidechain_checkpoint,
committee_definition,
);
Box::into_raw(Box::new(output_features))
}

/// Frees memory for a TariOutputFeatures
///
/// ## Arguments
/// `output_features` - The pointer to a TariOutputFeatures
///
/// ## Returns
/// `()` - Does not return a value, equivalent to void in C
///
/// # Safety
/// None
#[no_mangle]
pub unsafe extern "C" fn output_features_destroy(output_features: *mut TariOutputFeatures) {
if !output_features.is_null() {
Box::from_raw(output_features);
}
}

/// -------------------------------------------------------------------------------------------- ///

/// ----------------------------------- Seed Words ----------------------------------------------///

/// Create an empty instance of TariSeedWords
Expand Down Expand Up @@ -6950,6 +7094,102 @@ mod test {
}
}

#[test]
fn test_output_features_create_empty() {
unsafe {
let mut error = 0;
let error_ptr = &mut error as *mut c_int;

let version: c_uchar = 0;
let flags: c_uchar = 0;
let maturity: c_ulonglong = 20;
let recovery_byte: c_uchar = 1;
let metadata = Box::into_raw(Box::new(ByteVector(Vec::new())));
let unique_id = ptr::null_mut();
let parent_public_key = ptr::null_mut();

let output_features = output_features_create_from_bytes(
version,
flags,
maturity,
recovery_byte,
metadata,
unique_id,
parent_public_key,
error_ptr,
);
assert_eq!(error, 0);
assert_eq!((*output_features).version, OutputFeaturesVersion::V0);
assert_eq!((*output_features).flags, OutputFlags::from_bits(flags).unwrap());
assert_eq!((*output_features).maturity, maturity);
assert_eq!((*output_features).recovery_byte, recovery_byte);
assert!((*output_features).metadata.is_empty());
assert!((*output_features).unique_id.is_none());
assert!((*output_features).parent_public_key.is_none());

// These are DAN layer fields, we omit them
assert!((*output_features).asset.is_none());
assert!((*output_features).mint_non_fungible.is_none());
assert!((*output_features).sidechain_checkpoint.is_none());
assert!((*output_features).committee_definition.is_none());

output_features_destroy(output_features);
byte_vector_destroy(metadata);
}
}

#[test]
fn test_output_features_create_filled() {
unsafe {
let mut error = 0;
let error_ptr = &mut error as *mut c_int;

let version: c_uchar = OutputFeaturesVersion::V1.as_u8();
let flags: c_uchar = OutputFlags::COINBASE_OUTPUT.bits();
let maturity: c_ulonglong = 20;
let recovery_byte: c_uchar = 1;

let expected_metadata = vec![1; 1024];
let metadata = Box::into_raw(Box::new(ByteVector(expected_metadata.clone())));

let expected_unique_id = vec![0u8; 256];
let unique_id = Box::into_raw(Box::new(ByteVector(expected_unique_id.clone())));

let (_, public_key) = PublicKey::random_keypair(&mut OsRng);
let parent_public_key = Box::into_raw(Box::new(ByteVector(public_key.to_vec())));

let output_features = output_features_create_from_bytes(
version,
flags,
maturity,
recovery_byte,
metadata,
unique_id,
parent_public_key,
error_ptr,
);
assert_eq!(error, 0);
assert_eq!((*output_features).version, OutputFeaturesVersion::V1);
assert_eq!((*output_features).flags, OutputFlags::from_bits(flags).unwrap());
assert_eq!((*output_features).maturity, maturity);
assert_eq!((*output_features).recovery_byte, recovery_byte);
assert_eq!((*output_features).metadata, expected_metadata);
assert_eq!((*output_features).unique_id, Some(expected_unique_id));
assert_eq!((*output_features).parent_public_key, Some(public_key));

// These are DAN layer fields, we omit them
assert!((*output_features).asset.is_none());
assert!((*output_features).mint_non_fungible.is_none());
assert!((*output_features).sidechain_checkpoint.is_none());
assert!((*output_features).committee_definition.is_none());

output_features_destroy(output_features);
byte_vector_destroy(metadata);
byte_vector_destroy(unique_id);
byte_vector_destroy(parent_public_key);
}
}

#[test]
fn test_keys_dont_panic() {
unsafe {
Expand Down
17 changes: 17 additions & 0 deletions base_layer/wallet_ffi/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ struct TariCommitmentSignature *commitment_signature_create_from_bytes(
// Frees memory for a TariCommitmentSignature
void commitment_signature_destroy(struct TariCommitmentSignature *com_sig);

/// -------------------------------- Output Features --------------------------------------------- ///

// Creates a TariOutputFeatures from byte values
struct TariOutputFeatures *output_features_create_from_bytes(
unsigned char version,
unsigned char flags,
unsigned long long maturity,
unsigned char recovery_byte,
struct ByteVector *metadata,
struct ByteVector *unique_id,
struct ByteVector *parent_public_key,
int *error_out
);

// Frees memory for a TariOutputFeatures
void output_features_destroy(struct TariOutputFeatures *output_features);

/// -------------------------------- Seed Words -------------------------------------------------- ///
// Create an empty instance of TariSeedWords
struct TariSeedWords *seed_words_create();
Expand Down