diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7cbf907c..8e5551fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- spending_counter
- settings_new
- settings_get
+- fragment_from_raw
+- fragment_id
+- fragment_delete
+
+### Changed
+
+pending_transactions now returns transactions in order relative to the same
+wallet type instead of arbitrary order. First starting with deadalus/yoroi/free
+utxo keys (those are all exclusive) in order of creating, and then the account
+transactions, also in order of creation (and signing).
## [0.6.0-pre1]
diff --git a/bindings/wallet-c/src/lib.rs b/bindings/wallet-c/src/lib.rs
index 7fb99f10..3a732d0d 100644
--- a/bindings/wallet-c/src/lib.rs
+++ b/bindings/wallet-c/src/lib.rs
@@ -6,6 +6,7 @@ use std::{
};
pub use wallet::Settings as SettingsRust;
use wallet_core::c::{
+ fragment::{fragment_delete, fragment_from_raw, fragment_id},
symmetric_cipher_decrypt, vote, wallet_convert, wallet_convert_ignored,
wallet_convert_transactions_get, wallet_convert_transactions_size, wallet_delete_conversion,
wallet_delete_error, wallet_delete_proposal, wallet_delete_settings, wallet_delete_wallet,
@@ -13,8 +14,8 @@ use wallet_core::c::{
wallet_spending_counter, wallet_total_value, wallet_vote_cast,
};
use wallet_core::{
- Conversion as ConversionRust, Error as ErrorRust, Proposal as ProposalRust,
- Wallet as WalletRust,
+ Conversion as ConversionRust, Error as ErrorRust, Fragment as FragmentRust,
+ Proposal as ProposalRust, Wallet as WalletRust,
};
#[repr(C)]
@@ -26,6 +27,8 @@ pub struct Conversion {}
#[repr(C)]
pub struct Proposal {}
#[repr(C)]
+pub struct Fragment;
+#[repr(C)]
pub struct Error {}
#[repr(C)]
@@ -35,6 +38,7 @@ pub type WalletPtr = *mut Wallet;
pub type SettingsPtr = *mut Settings;
pub type ConversionPtr = *mut Conversion;
pub type ProposalPtr = *mut Proposal;
+pub type FragmentPtr = *mut Fragment;
pub type ErrorPtr = *mut Error;
pub type EncryptingVoteKeyPtr = *mut EncryptingVoteKey;
@@ -657,6 +661,70 @@ pub unsafe extern "C" fn iohk_jormungandr_wallet_error_details(error: ErrorPtr)
}
}
+/// deserialize a fragment from bytes
+///
+/// # Parameters
+///
+/// * `buffer` -- a pointer to the serialized fragment bytes.
+/// * `buffer_length` -- the length of the serialized fragment bytes array.
+/// * `fragment` -- the location of the pointer to the deserialized fragemnt.
+///
+/// # Errors
+///
+/// This functions may fail if:
+///
+/// * `buffer` is a null pointer.
+/// * `fragment` is a null pointer.
+/// * `buffer` contains invalid fragment bytes.
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors
+///
+/// Don't forget to delete the fragment object with `iohk_jormungandr_delete_fragment`.
+#[no_mangle]
+pub unsafe extern "C" fn iohk_jormungandr_fragment_from_raw(
+ buffer: *const u8,
+ buffer_length: usize,
+ fragment_out: *mut FragmentPtr,
+) -> ErrorPtr {
+ fragment_from_raw(
+ buffer,
+ buffer_length,
+ fragment_out as *mut *mut FragmentRust,
+ )
+ .into_c_api() as ErrorPtr
+}
+
+/// get the ID of the provided fragment
+///
+/// # Parameters
+///
+/// * `fragment` -- a pointer to fragment.
+/// * `fragment_id_out` -- a pointer to a pre-allocated 32 bytes array.
+///
+/// # Errors
+///
+/// This function would return an error if either of the provided pointers is null.
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors.
+///
+/// `fragment_id_out` is expected to be an already allocated 32 byte array. Doing otherwise may
+/// potentially result into an undefined behavior.
+#[no_mangle]
+pub unsafe extern "C" fn iohk_jormungandr_fragment_id(
+ fragment: FragmentPtr,
+ fragment_id_out: *mut u8,
+) -> ErrorPtr {
+ fragment_id(fragment as *mut FragmentRust, fragment_id_out).into_c_api() as ErrorPtr
+}
+
/// Delete a null terminated string that was allocated by this library
///
/// # Safety
@@ -754,3 +822,15 @@ pub extern "C" fn iohk_jormungandr_wallet_delete_conversion(conversion: Conversi
pub extern "C" fn iohk_jormungandr_wallet_delete_proposal(proposal: ProposalPtr) {
wallet_delete_proposal(proposal as *mut ProposalRust)
}
+
+/// delete the pointer
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors
+#[no_mangle]
+pub unsafe extern "C" fn iohk_jormungandr_delete_fragment(fragment: FragmentPtr) {
+ fragment_delete(fragment as *mut FragmentRust);
+}
diff --git a/bindings/wallet-c/wallet.h b/bindings/wallet-c/wallet.h
index ccbbc8db..4e6a918c 100644
--- a/bindings/wallet-c/wallet.h
+++ b/bindings/wallet-c/wallet.h
@@ -10,7 +10,7 @@
#ifndef IOHK_CHAIN_WALLET_LIBC_
#define IOHK_CHAIN_WALLET_LIBC_
-/* Generated with cbindgen:0.18.0 */
+/* Generated with cbindgen:0.19.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
@@ -25,6 +25,13 @@ typedef enum Discrimination
Discrimination_Test,
} Discrimination;
+typedef struct Fragment
+{
+
+} Fragment;
+
+typedef struct Fragment *FragmentPtr;
+
typedef struct Error
{
@@ -86,6 +93,70 @@ typedef struct LinearFee
struct PerVoteCertificateFee per_vote_certificate_fees;
} LinearFee;
+/**
+ * delete the pointer
+ *
+ * # Safety
+ *
+ * This function dereference raw pointers. Even though
+ * the function checks if the pointers are null. Mind not to put random values
+ * in or you may see unexpected behaviors
+ */
+void iohk_jormungandr_delete_fragment(FragmentPtr fragment);
+
+/**
+ * deserialize a fragment from bytes
+ *
+ * # Parameters
+ *
+ * * `buffer` -- a pointer to the serialized fragment bytes.
+ * * `buffer_length` -- the length of the serialized fragment bytes array.
+ * * `fragment` -- the location of the pointer to the deserialized fragemnt.
+ *
+ * # Errors
+ *
+ * This functions may fail if:
+ *
+ * * `buffer` is a null pointer.
+ * * `fragment` is a null pointer.
+ * * `buffer` contains invalid fragment bytes.
+ *
+ * # Safety
+ *
+ * This function dereference raw pointers. Even though
+ * the function checks if the pointers are null. Mind not to put random values
+ * in or you may see unexpected behaviors
+ *
+ * Don't forget to delete the fragment object with `iohk_jormungandr_delete_fragment`.
+ */
+ErrorPtr iohk_jormungandr_fragment_from_raw(const uint8_t *buffer,
+ uintptr_t buffer_length,
+ FragmentPtr *fragment_out);
+
+/**
+ * get the ID of the provided fragment
+ *
+ * # Parameters
+ *
+ * * `fragment` -- a pointer to fragment.
+ * * `fragment_id_out` -- a pointer to a pre-allocated 32 bytes array.
+ *
+ * # Errors
+ *
+ * This function would return an error if either of the provided pointers is null.
+ *
+ * # Safety
+ *
+ * This function dereference raw pointers. Even though
+ * the function checks if the pointers are null. Mind not to put random values
+ * in or you may see unexpected behaviors.
+ *
+ * `fragment_id_out` is expected to be an already allocated 32 byte array. Doing otherwise may
+ * potentially result into an undefined behavior.
+ */
+ErrorPtr iohk_jormungandr_fragment_id(FragmentPtr fragment,
+ uint8_t *fragment_id_out);
+
/**
* decrypt payload of the wallet transfer protocol
*
diff --git a/bindings/wallet-cordova/build_jni.py b/bindings/wallet-cordova/build_jni.py
index b5e29290..6ed60883 100755
--- a/bindings/wallet-cordova/build_jni.py
+++ b/bindings/wallet-cordova/build_jni.py
@@ -21,7 +21,7 @@
def run():
for rust_target, android_target in targets.items():
- out = subprocess.run(["cargo", "rustc", "--release", "--target",
+ out = subprocess.run(["cross", "rustc", "--release", "--target",
rust_target, "-p" "wallet-jni", "--", "-C", "lto"])
if out.returncode != 0:
diff --git a/bindings/wallet-cordova/plugin.xml b/bindings/wallet-cordova/plugin.xml
index f4b149ce..4fdcc80d 100644
--- a/bindings/wallet-cordova/plugin.xml
+++ b/bindings/wallet-cordova/plugin.xml
@@ -27,6 +27,7 @@
+
diff --git a/bindings/wallet-cordova/src/android/WalletPlugin.java b/bindings/wallet-cordova/src/android/WalletPlugin.java
index cfed004b..22731385 100644
--- a/bindings/wallet-cordova/src/android/WalletPlugin.java
+++ b/bindings/wallet-cordova/src/android/WalletPlugin.java
@@ -10,6 +10,7 @@
import com.iohk.jormungandrwallet.Conversion;
import com.iohk.jormungandrwallet.Proposal;
import com.iohk.jormungandrwallet.SymmetricCipher;
+import com.iohk.jormungandrwallet.Fragment;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
@@ -120,6 +121,9 @@ public boolean execute(final String action, final CordovaArgs args, final Callba
case "SETTINGS_GET":
settingsGet(args, callbackContext);
break;
+ case "FRAGMENT_ID":
+ fragmentId(args, callbackContext);
+ break;
case "WALLET_DELETE":
walletDelete(args, callbackContext);
break;
@@ -467,6 +471,19 @@ private void walletId(final CordovaArgs args, final CallbackContext callbackCont
}
}
+ private void fragmentId(final CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
+ final byte[] transaction = args.getArrayBuffer(0);
+
+ try {
+ final long fragmentPtr = Fragment.fromBytes(transaction);
+ final byte[] id = Fragment.id(fragmentPtr);
+ Fragment.delete(fragmentPtr);
+ callbackContext.success(id);
+ } catch (final Exception e) {
+ callbackContext.error(e.getMessage());
+ }
+ }
+
private void walletDelete(final CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
final Long walletPtr = args.getLong(0);
diff --git a/bindings/wallet-cordova/src/ios/WalletPlugin.h b/bindings/wallet-cordova/src/ios/WalletPlugin.h
index 5438f4eb..fb132b55 100644
--- a/bindings/wallet-cordova/src/ios/WalletPlugin.h
+++ b/bindings/wallet-cordova/src/ios/WalletPlugin.h
@@ -26,6 +26,8 @@
- (void)SETTINGS_NEW:(CDVInvokedUrlCommand*)command;
- (void)SETTINGS_GET:(CDVInvokedUrlCommand*)command;
+- (void)FRAGMENT_ID:(CDVInvokedUrlCommand*)command;
+
- (void)WALLET_DELETE:(CDVInvokedUrlCommand*)command;
- (void)SETTINGS_DELETE:(CDVInvokedUrlCommand*)command;
- (void)CONVERSION_DELETE:(CDVInvokedUrlCommand*)command;
diff --git a/bindings/wallet-cordova/src/ios/WalletPlugin.m b/bindings/wallet-cordova/src/ios/WalletPlugin.m
index 8b1a82af..3da408d2 100644
--- a/bindings/wallet-cordova/src/ios/WalletPlugin.m
+++ b/bindings/wallet-cordova/src/ios/WalletPlugin.m
@@ -578,6 +578,43 @@ - (void)SETTINGS_GET:(CDVInvokedUrlCommand*)command
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
+- (void)FRAGMENT_ID:(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = nil;
+
+ NSData* fragment_raw = [command.arguments objectAtIndex:0];
+
+ if ([fragment_raw isEqual:[NSNull null]]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
+ messageAsString:@"missing argument"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+
+ FragmentPtr fragment_ptr = nil;
+ ErrorPtr result_fragment_from_raw =
+ iohk_jormungandr_fragment_from_raw(fragment_raw.bytes, fragment_raw.length, &fragment_ptr);
+
+ if (result_fragment_from_raw != nil) {
+ pluginResult = jormungandr_error_to_plugin_result(result_fragment_from_raw);
+ } else {
+ uint8_t fragment_id[32];
+ ErrorPtr result_fragment_id = iohk_jormungandr_fragment_id(fragment_ptr, fragment_id);
+
+ if (result_fragment_id != nil) {
+ pluginResult = jormungandr_error_to_plugin_result(result_fragment_id);
+ } else {
+ NSData* returnValue = [NSData dataWithBytes:fragment_id length:32];
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsArrayBuffer:returnValue];
+ }
+
+ iohk_jormungandr_delete_fragment(fragment_ptr);
+ }
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
- (void)WALLET_DELETE:(CDVInvokedUrlCommand*)command
{
NSString* wallet_ptr_raw = [command.arguments objectAtIndex:0];
diff --git a/bindings/wallet-cordova/tests/src/main.js b/bindings/wallet-cordova/tests/src/main.js
index d5ab9699..f78860dc 100644
--- a/bindings/wallet-cordova/tests/src/main.js
+++ b/bindings/wallet-cordova/tests/src/main.js
@@ -45,6 +45,7 @@ const pendingTransactionsGet = promisifyP(primitives.pendingTransactionsGet);
const symmetricCipherDecrypt = promisifyP(primitives.symmetricCipherDecrypt);
const settingsGet = promisifyP(primitives.settingsGet);
const settingsNew = promisifyP(primitives.settingsNew);
+const fragmentId = promisifyP(primitives.fragmentId);
const tests = [
@@ -159,6 +160,9 @@ const tests = [
expect(await spendingCounter(walletPtr)).toBe(1);
+ const pending = await getPendingTransactions(walletPtr);
+ expect(pending.length).toBe(1);
+
await deleteSettings(settingsPtr);
await deleteWallet(walletPtr);
await deleteProposal(proposalPtr);
@@ -295,6 +299,36 @@ const tests = [
await deleteSettings(settingsPtr);
}],
+ ['get vote fragment id', async function () {
+ const array = new Array(32);
+ for (let index = 0; index < array.length; index++) {
+ array[index] = index;
+ }
+
+ const votePlanId = new Uint8Array(array);
+ const index = 0;
+ const numChoices = 3;
+
+ const proposalPtr = await proposalNewPublic(votePlanId, index, numChoices);
+ const walletPtr = await restoreWallet(YOROI_WALLET);
+ const settingsPtr = await retrieveFunds(walletPtr, hexStringToBytes(BLOCK0));
+ await walletSetState(walletPtr, 1000000, 0);
+
+ const tx1 = await walletVote(walletPtr, settingsPtr, proposalPtr, 1);
+ const tx2 = await walletVote(walletPtr, settingsPtr, proposalPtr, 2);
+
+ const id1 = await fragmentId(new Uint8Array(tx1));
+ const id2 = await fragmentId(new Uint8Array(tx2));
+
+ const pendingTransactions = await getPendingTransactions(walletPtr);
+
+ expect(uint8ArrayEquals(id1, pendingTransactions[0])).toBe(true);
+ expect(uint8ArrayEquals(id2, pendingTransactions[0])).toBe(true);
+
+ await deleteSettings(settingsPtr);
+ await deleteWallet(walletPtr);
+ await deleteProposal(proposalPtr);
+ }],
]
exports.defineAutoTests = function () {
diff --git a/bindings/wallet-cordova/www/wallet.js b/bindings/wallet-cordova/www/wallet.js
index 11be8146..b5148763 100644
--- a/bindings/wallet-cordova/www/wallet.js
+++ b/bindings/wallet-cordova/www/wallet.js
@@ -30,6 +30,7 @@ const PENDING_TRANSACTIONS_SIZE = 'PENDING_TRANSACTIONS_SIZE';
const SYMMETRIC_CIPHER_DECRYPT = 'SYMMETRIC_CIPHER_DECRYPT';
const SETTINGS_NEW = 'SETTINGS_NEW';
const SETTINGS_GET = 'SETTINGS_GET';
+const FRAGMENT_ID = 'FRAGMENT_ID';
const VOTE_PLAN_ID_LENGTH = 32;
const FRAGMENT_ID_LENGTH = 32;
@@ -91,6 +92,11 @@ var plugin = {
TEST: 1
},
+ /**
+ * @callback TransactionIdCallback
+ * @param {Uint8Array} id
+ */
+
/**
* @param {string} mnemonics a string with the mnemonic phrase
* @param {pointerCallback} successCallback on success returns a pointer to a Wallet object
@@ -255,7 +261,7 @@ var plugin = {
/**
* @param {string} ptr a pointer to a PendingTransactions object obtained with walletPendingTransactions
* @param {number} index an index (starting from 0). Use pendingTransactionsSize to get the upper bound
- * @param {function} successCallback callback that receives a transaction in binary form
+ * @param {TransactionIdCallback} successCallback callback that receives a transaction id in binary form
* @param {errorCallback} errorCallback this function can fail if the index is out of range
*/
pendingTransactionsGet: function (ptr, index, successCallback, errorCallback) {
@@ -386,6 +392,18 @@ var plugin = {
exec(decodeBase64, errorCallback, NATIVE_CLASS_NAME, SETTINGS_GET, [settingsPtr]);
},
+ /**
+ * @param {Uint8Array} transaction
+ * @param {TransactionIdCallback} successCallback
+ * @param {errorCallback} errorCallback
+ */
+ fragmentId: function (transaction, successCallback, errorCallback) {
+ argscheck.checkArgs('*ff', 'transactionId', arguments);
+ checkUint8Array({ name: 'transaction', testee: transaction });
+
+ exec(successCallback, errorCallback, NATIVE_CLASS_NAME, FRAGMENT_ID, [transaction.buffer]);
+ },
+
/**
* @param {string} ptr a pointer to a Wallet obtained with walletRestore
* @param {function} successCallback indicates success. Does not return anything.
diff --git a/bindings/wallet-core/src/c/fragment.rs b/bindings/wallet-core/src/c/fragment.rs
new file mode 100644
index 00000000..be44e6c7
--- /dev/null
+++ b/bindings/wallet-core/src/c/fragment.rs
@@ -0,0 +1,73 @@
+use crate::{Error, Result};
+use chain_core::property::Deserialize;
+use chain_impl_mockchain::fragment::{Fragment, FragmentRaw};
+use core::slice;
+
+use super::{FragmentPtr, NulPtr, FRAGMENT_ID_LENGTH};
+
+/// # Safety
+///
+/// buffer must be non null and point to buffer_length bytes of valid memory.
+///
+pub unsafe fn fragment_from_raw(
+ buffer: *const u8,
+ buffer_length: usize,
+ fragment_out: *mut FragmentPtr,
+) -> Result {
+ if buffer.is_null() {
+ return Error::invalid_input("buffer").with(NulPtr).into();
+ }
+
+ let fragment_out_ref = non_null_mut!(fragment_out);
+
+ let bytes = slice::from_raw_parts(buffer, buffer_length);
+
+ let raw = match FragmentRaw::deserialize(bytes) {
+ Ok(raw) => raw,
+ Err(_e) => return Error::invalid_fragment().into(),
+ };
+
+ let fragment = match Fragment::from_raw(&raw) {
+ Ok(fragment) => fragment,
+ Err(_e) => return Error::invalid_fragment().into(),
+ };
+
+ let fragment = Box::new(fragment);
+
+ *fragment_out_ref = Box::into_raw(fragment);
+
+ Result::success()
+}
+
+/// # Safety
+///
+/// fragment_ptr must be a pointer to memory allocated by this library, for
+/// example, with `fragment_from_raw`
+/// id_out must point to FRAGMENT_ID_LENGTH bytes of valid allocated writable
+/// memory
+/// This function checks for null pointers
+///
+pub unsafe fn fragment_id(fragment_ptr: FragmentPtr, id_out: *mut u8) -> Result {
+ let fragment = non_null!(fragment_ptr);
+
+ let id = fragment.hash();
+
+ let bytes = id.as_bytes();
+
+ assert_eq!(bytes.len(), FRAGMENT_ID_LENGTH);
+
+ std::ptr::copy(bytes.as_ptr(), id_out, bytes.len());
+
+ Result::success()
+}
+
+/// # Safety
+///
+/// This function checks for null pointers, but take care that fragment_ptr was
+/// previously allocated by this library for example with fragment_from_raw
+///
+pub unsafe fn fragment_delete(fragment_ptr: FragmentPtr) {
+ if !fragment_ptr.is_null() {
+ Box::from_raw(fragment_ptr as FragmentPtr);
+ }
+}
diff --git a/bindings/wallet-core/src/c/mod.rs b/bindings/wallet-core/src/c/mod.rs
index f60a9bdf..e2cdac8c 100644
--- a/bindings/wallet-core/src/c/mod.rs
+++ b/bindings/wallet-core/src/c/mod.rs
@@ -2,11 +2,12 @@
//! C style bindings that we have (wallet-c, wallet-jni...)
#[macro_use]
mod macros;
+pub mod fragment;
pub mod settings;
pub mod vote;
use crate::{Conversion, Error, Proposal, Result, Wallet};
-use chain_impl_mockchain::{transaction::Input, value::Value, vote::Choice};
+use chain_impl_mockchain::{fragment::Fragment, transaction::Input, value::Value, vote::Choice};
use std::convert::TryInto;
use thiserror::Error;
@@ -18,6 +19,7 @@ pub type ConversionPtr = *mut Conversion;
pub type ProposalPtr = *mut Proposal;
pub type ErrorPtr = *mut Error;
pub type PendingTransactionsPtr = *mut PendingTransactions;
+pub type FragmentPtr = *mut Fragment;
#[derive(Debug, Error)]
#[error("null pointer")]
@@ -328,12 +330,7 @@ pub unsafe fn wallet_pending_transactions(
let wallet = non_null!(wallet);
let pending_transactions = PendingTransactions {
- fragment_ids: wallet
- .pending_transactions()
- .iter()
- .cloned()
- .collect::>()
- .into_boxed_slice(),
+ fragment_ids: wallet.pending_transactions().into_boxed_slice(),
};
*pending_transactions_out =
diff --git a/bindings/wallet-core/src/error.rs b/bindings/wallet-core/src/error.rs
index 20dd09a9..eda80680 100644
--- a/bindings/wallet-core/src/error.rs
+++ b/bindings/wallet-core/src/error.rs
@@ -64,6 +64,9 @@ pub enum ErrorCode {
/// wallet out of funds
NotEnoughFunds = 9,
+
+ /// invalid fragment
+ InvalidFragment = 10,
}
#[derive(Debug)]
@@ -100,6 +103,9 @@ pub enum ErrorKind {
/// wallet out of funds
NotEnoughFunds,
+
+ /// invalid fragment
+ InvalidFragment,
}
impl ErrorKind {
@@ -118,6 +124,7 @@ impl ErrorKind {
Self::SymmetricCipherInvalidPassword => ErrorCode::SymmetricCipherInvalidPassword,
Self::InvalidVoteEncryptionKey => ErrorCode::InvalidVoteEncryptionKey,
Self::NotEnoughFunds => ErrorCode::NotEnoughFunds,
+ Self::InvalidFragment => ErrorCode::InvalidFragment,
}
}
}
@@ -226,6 +233,13 @@ impl Error {
}
}
+ pub fn invalid_fragment() -> Self {
+ Self {
+ kind: ErrorKind::InvalidFragment,
+ details: None,
+ }
+ }
+
/// set some details to the `Result` object if the `Result` is of
/// error kind
///
@@ -369,6 +383,7 @@ impl Display for ErrorKind {
Self::SymmetricCipherInvalidPassword => f.write_str("invalid decryption password"),
Self::InvalidVoteEncryptionKey => f.write_str("invalid vote encryption key"),
Self::NotEnoughFunds => f.write_str("not enough funds to create transaction"),
+ Self::InvalidFragment => f.write_str("invalid fragment"),
}
}
}
diff --git a/bindings/wallet-core/src/lib.rs b/bindings/wallet-core/src/lib.rs
index da1b46f5..29b84aa4 100644
--- a/bindings/wallet-core/src/lib.rs
+++ b/bindings/wallet-core/src/lib.rs
@@ -12,7 +12,7 @@ pub use self::{
};
pub use ::wallet::Settings;
pub use chain_impl_mockchain::{
- fragment::FragmentId,
+ fragment::{Fragment, FragmentId},
value::Value,
vote::{Choice, Options, PayloadType},
};
diff --git a/bindings/wallet-core/src/wallet.rs b/bindings/wallet-core/src/wallet.rs
index 52bade94..196fe873 100644
--- a/bindings/wallet-core/src/wallet.rs
+++ b/bindings/wallet-core/src/wallet.rs
@@ -244,29 +244,39 @@ impl Wallet {
///
/// TODO: this might need to be updated to have a more user friendly
/// API. Currently do this for simplicity
- pub fn pending_transactions(&self) -> std::collections::HashSet {
- let mut set = std::collections::HashSet::new();
+ pub fn pending_transactions(&self) -> Vec {
+ let mut check = std::collections::HashSet::new();
+ let mut result = vec![];
if let Some(daedalus) = &self.daedalus {
for id in daedalus.pending_transactions() {
- set.insert(*id);
+ if check.insert(*id) {
+ result.push(*id);
+ }
}
}
if let Some(icarus) = &self.icarus {
for id in icarus.pending_transactions() {
- set.insert(*id);
+ if check.insert(*id) {
+ result.push(*id);
+ }
}
}
for id in self.free_keys.pending_transactions() {
- set.insert(*id);
+ if check.insert(*id) {
+ result.push(*id);
+ }
}
for id in self.account.pending_transactions() {
- set.insert(*id);
+ if check.insert(*id) {
+ result.push(*id);
+ }
}
- set
+
+ result
}
/// remove a given pending transaction returning the associated Inputs
diff --git a/bindings/wallet-jni/java/WalletTest.java b/bindings/wallet-jni/java/WalletTest.java
index 72dce911..3dadc409 100644
--- a/bindings/wallet-jni/java/WalletTest.java
+++ b/bindings/wallet-jni/java/WalletTest.java
@@ -1,10 +1,11 @@
import com.iohk.jormungandrwallet.Wallet;
import com.iohk.jormungandrwallet.Settings;
import com.iohk.jormungandrwallet.Conversion;
+import com.iohk.jormungandrwallet.Fragment;
import com.iohk.jormungandrwallet.Proposal;
import com.iohk.jormungandrwallet.PendingTransactions;
import com.iohk.jormungandrwallet.SymmetricCipher;
-import com.iohk.jormungandrwallet.Settings;
+import com.iohk.jormungandrwallet.Fragment;
import java.util.Properties;
import java.util.Enumeration;
@@ -74,8 +75,8 @@ public void extractSettings() throws IOException {
public void buildSettings() throws IOException {
final byte[] blockId = hexStringToByteArray("182764b45bae25cc466143de8107618b37f0d28fe3daa0a0d39fd0ab5a2061e1");
final Settings.Discrimination discrimination = Settings.Discrimination.TEST;
- final Settings.LinearFees expectedFees = new Settings.LinearFees(1, 2, 3, new Settings.PerCertificateFee(4, 5, 6),
- new Settings.PerVoteCertificateFee(7, 8));
+ final Settings.LinearFees expectedFees = new Settings.LinearFees(1, 2, 3,
+ new Settings.PerCertificateFee(4, 5, 6), new Settings.PerVoteCertificateFee(7, 8));
Settings.PerCertificateFee test = new Settings.PerCertificateFee(4, 5, 6);
@@ -96,8 +97,10 @@ public void buildSettings() throws IOException {
assertEquals(fees.perCertificateFee.certificateOwnerStakeDelegation,
expectedFees.perCertificateFee.certificateOwnerStakeDelegation);
- assertEquals(fees.perVoteCertificateFee.certificateVotePlan, expectedFees.perVoteCertificateFee.certificateVotePlan);
- assertEquals(fees.perVoteCertificateFee.certificateVoteCast, expectedFees.perVoteCertificateFee.certificateVoteCast);
+ assertEquals(fees.perVoteCertificateFee.certificateVotePlan,
+ expectedFees.perVoteCertificateFee.certificateVotePlan);
+ assertEquals(fees.perVoteCertificateFee.certificateVoteCast,
+ expectedFees.perVoteCertificateFee.certificateVoteCast);
final Settings.Discrimination foundDiscrimination = Settings.discrimination(settingsPtr);
@@ -301,6 +304,47 @@ public void confirmVoteCast() throws IOException {
}
}
+ @Test
+ public void fragmentId() throws IOException {
+ final long walletPtr = Wallet.recover(
+ "neck bulb teach illegal soul cry monitor claw amount boring provide village rival draft stone");
+
+ final byte[] block0 = Files.readAllBytes(Paths.get("../../../test-vectors/block0"));
+
+ final long settingsPtr = Wallet.initialFunds(walletPtr, block0);
+
+ final byte[] id = new byte[Proposal.ID_SIZE];
+ final long proposalPtr = Proposal.withPublicPayload(id, 0, 3);
+
+ Wallet.setState(walletPtr, 10000000, 0);
+
+ try {
+ final byte[] transaction = Wallet.voteCast(walletPtr, settingsPtr, proposalPtr, 1);
+
+ final long fragment = Fragment.fromBytes(transaction);
+ final byte[] fragmentId = Fragment.id(fragment);
+ Fragment.delete(fragment);
+
+ final long pending = Wallet.pendingTransactions(walletPtr);
+
+ final int sizeBefore = PendingTransactions.len(pending);
+
+ final byte[] expectedFragmentId = PendingTransactions.get(pending, 0);
+
+ for (int i = 0; i < fragmentId.length; i++) {
+ assertEquals(fragmentId[i], expectedFragmentId[i]);
+ }
+
+ PendingTransactions.delete(pending);
+ } catch (final Exception e) {
+ Proposal.delete(proposalPtr);
+ Settings.delete(settingsPtr);
+ Wallet.delete(walletPtr);
+ System.out.println(e.getMessage());
+ throw e;
+ }
+ }
+
@Test
public void privateVoteCast() throws IOException {
final long walletPtr = Wallet.recover(
diff --git a/bindings/wallet-jni/java/com/iohk/jormungandrwallet/Fragment.java b/bindings/wallet-jni/java/com/iohk/jormungandrwallet/Fragment.java
new file mode 100644
index 00000000..701294d1
--- /dev/null
+++ b/bindings/wallet-jni/java/com/iohk/jormungandrwallet/Fragment.java
@@ -0,0 +1,13 @@
+package com.iohk.jormungandrwallet;
+
+public class Fragment {
+ static {
+ System.loadLibrary("wallet_jni");
+ }
+
+ public native static long fromBytes(byte[] buffer);
+
+ public native static byte[] id(long fragmentPtr);
+
+ public native static void delete (long fragmentPtr);
+}
diff --git a/bindings/wallet-jni/src/lib.rs b/bindings/wallet-jni/src/lib.rs
index 8dcf6116..58a894d6 100644
--- a/bindings/wallet-jni/src/lib.rs
+++ b/bindings/wallet-jni/src/lib.rs
@@ -3,11 +3,14 @@ use jni::sys::{jbyte, jbyteArray, jint, jlong};
use jni::JNIEnv;
use std::convert::TryInto;
use std::ptr::{null, null_mut};
-use wallet_core::c::settings::{
- settings_block0_hash, settings_discrimination, settings_fees, settings_new, Discrimination,
- LinearFee, PerCertificateFee, PerVoteCertificateFee,
-};
use wallet_core::c::*;
+use wallet_core::c::{
+ fragment::{fragment_from_raw, fragment_id},
+ settings::{
+ settings_block0_hash, settings_discrimination, settings_fees, settings_new, Discrimination,
+ LinearFee, PerCertificateFee, PerVoteCertificateFee,
+ },
+};
///
/// # Safety
@@ -1027,3 +1030,91 @@ pub extern "system" fn Java_com_iohk_jormungandrwallet_SymmetricCipher_decrypt(
}
}
}
+
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors
+///
+#[no_mangle]
+pub unsafe extern "system" fn Java_com_iohk_jormungandrwallet_Fragment_fromBytes(
+ env: JNIEnv,
+ _: JClass,
+ buffer: jbyteArray,
+) -> jlong {
+ let len = env
+ .get_array_length(buffer)
+ .expect("Couldn't get block0 array length") as usize;
+
+ let mut bytes = vec![0i8; len as usize];
+
+ env.get_byte_array_region(buffer, 0, &mut bytes).unwrap();
+
+ let mut ptr: FragmentPtr = null_mut();
+
+ let result = fragment_from_raw(
+ bytes.as_ptr().cast::(),
+ len,
+ &mut ptr as *mut FragmentPtr,
+ );
+
+ if let Some(error) = result.error() {
+ let _ = env.throw(error.to_string());
+ }
+
+ ptr as jlong
+}
+
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors
+///
+#[no_mangle]
+pub unsafe extern "system" fn Java_com_iohk_jormungandrwallet_Fragment_id(
+ env: JNIEnv,
+ _: JClass,
+ fragment: jlong,
+) -> jbyteArray {
+ let mut id = [0u8; FRAGMENT_ID_LENGTH];
+
+ let result = fragment_id(fragment as FragmentPtr, id.as_mut_ptr());
+
+ let array = env
+ .new_byte_array(id.len() as jint)
+ .expect("Failed to create new byte array");
+
+ match result.error() {
+ None => {
+ let slice = std::slice::from_raw_parts(id.as_ptr() as *const jbyte, id.len());
+
+ env.set_byte_array_region(array, 0, slice)
+ .expect("Couldn't copy array to jvm");
+ }
+ Some(error) => {
+ let _ = env.throw(error.to_string());
+ }
+ };
+
+ array
+}
+
+///
+/// # Safety
+///
+/// This function dereference raw pointers. Even though
+/// the function checks if the pointers are null. Mind not to put random values
+/// in or you may see unexpected behaviors
+///
+#[no_mangle]
+pub unsafe extern "system" fn Java_com_iohk_jormungandrwallet_Fragment_delete(
+ _env: JNIEnv,
+ _: JClass,
+ fragment: jlong,
+) {
+ fragment::fragment_delete(fragment as FragmentPtr);
+}