#![forbid(unsafe_code)]
pub mod cidr_block;
mod correlation_id;
pub mod errors;
pub mod serde_hex;
mod transaction_id;
pub use cidr_block::{CidrBlock, CidrBlockParseErr};
pub use correlation_id::{CorrelationId, CorrelationIdError};
#[cfg(feature = "runtime-conversions")]
pub use substrate_mapping::{
hash_substrate_encodeable, test_helpers as substrate_test_helpers, to_platform::*,
TxnConversionError,
};
pub use transaction_id::{TransactionId, TransactionIdError};
use chrono::{DateTime, LocalResult, TimeZone, Utc};
use derive_more::Constructor;
pub mod public_key;
pub use errors::EncryptionError;
pub use public_key::PublicKey;
use serde::{Deserialize, Serialize};
use snafu::{ResultExt, Snafu};
use std::{
any::type_name,
collections::HashMap,
convert::{TryFrom, TryInto},
fmt::{Debug, Display, Error, Formatter},
str::FromStr,
};
use strum::{EnumString, ToString};
use wasm_hash_verifier::XandHash;
use xand_address::Address;
pub const DEFAULT_PAGE_SIZE: u32 = 50;
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub struct EncryptionKey([u8; 32]);
impl EncryptionKey {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl Display for EncryptionKey {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let encoded_key = bs58::encode(self.0).into_string();
Display::fmt(&encoded_key, f)
}
}
impl TryFrom<String> for EncryptionKey {
type Error = EncryptionKeyError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let key = bs58::decode(&value).into_vec().context(Base58Encode)?;
EncryptionKey::try_from(key.as_slice())
}
}
impl FromStr for EncryptionKey {
type Err = EncryptionKeyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.to_owned().try_into()
}
}
impl TryFrom<&[u8]> for EncryptionKey {
type Error = EncryptionKeyError;
fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
if slice.len() != 32 {
Err(EncryptionKeyError::InvalidKeyLength)
} else {
let mut bytes: [u8; 32] = [0; 32];
bytes.copy_from_slice(slice);
Ok(EncryptionKey(bytes))
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, snafu::Snafu)]
pub enum EncryptionKeyError {
#[snafu(display("Encryption key wasn't proper base 58: {:?}", source))]
Base58Encode {
#[serde(serialize_with = "xand_utils::snafu_extensions::debug_serialize")]
source: bs58::decode::Error,
},
#[snafu(display("Invalid encryption key length, expected 32 bytes"))]
InvalidKeyLength,
}
#[derive(Serialize, Deserialize, Debug, EnumString, Hash, PartialEq, Eq, Clone, ToString)]
pub enum TransactionStatus {
Unknown,
Pending,
Invalid(String),
Committed,
Finalized,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Transaction {
pub signer_address: Address,
pub transaction_id: TransactionId,
pub status: TransactionStatus,
pub txn: XandTransaction,
pub timestamp: DateTime<Utc>,
}
impl Transaction {
pub fn is_financial_event(&self) -> bool {
matches!(
self.txn,
XandTransaction::CreateRequest(_)
| XandTransaction::CashConfirmation(_)
| XandTransaction::RedeemRequest(_)
| XandTransaction::RedeemFulfillment(_)
| XandTransaction::Send(_)
)
}
pub fn from_xand_txn(
id: TransactionId,
txn: XandTransaction,
signer: Address,
status: TransactionStatus,
timestamp: DateTime<Utc>,
) -> Self {
Self {
transaction_id: id,
signer_address: signer,
status,
txn,
timestamp,
}
}
pub fn get_amount(&self) -> Option<u64> {
match &self.txn {
XandTransaction::Send(data) => Some(data.amount_in_minor_unit),
XandTransaction::CreateRequest(data) => Some(data.amount_in_minor_unit),
XandTransaction::RedeemRequest(data) => Some(data.amount_in_minor_unit),
_ => None,
}
}
pub fn get_correlation_id(&self) -> Option<&[u8]> {
match &self.txn {
XandTransaction::CreateRequest(data) => Some(data.correlation_id.as_bytes()),
XandTransaction::RedeemRequest(data) => Some(data.correlation_id.as_bytes()),
XandTransaction::CreateCancellation(data) => Some(data.correlation_id.as_bytes()),
XandTransaction::CashConfirmation(data) => Some(data.correlation_id.as_bytes()),
XandTransaction::RedeemFulfillment(data) => Some(data.correlation_id.as_bytes()),
_ => None,
}
}
pub fn get_destination_addr(&self) -> Option<Address> {
match &self.txn {
XandTransaction::RegisterMember(data) => Some(data.address.clone()),
XandTransaction::SetTrust(data) => Some(data.address.clone()),
XandTransaction::Send(data) => Some(data.destination_account.clone()),
XandTransaction::CreateRequest(_) => Some(self.signer_address.clone()),
_ => None,
}
}
pub fn get_bank_info(&self) -> Option<&BankAccountInfo> {
match &self.txn {
XandTransaction::CreateRequest(data) => Some(&data.account),
XandTransaction::RedeemRequest(data) => Some(&data.account),
_ => None,
}
}
pub fn timestamp_from_unix_time_millis(t: i64) -> Option<DateTime<Utc>> {
match Utc.timestamp_millis_opt(t) {
LocalResult::None => None,
LocalResult::Single(date_time) => Some(date_time),
LocalResult::Ambiguous(_, _) => None,
}
}
}
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
strum::Display,
strum::EnumDiscriminants,
)]
#[serde(tag = "serde_operation")]
#[strum_discriminants(name(TransactionType))]
pub enum XandTransaction {
RegisterMember(RegisterAccountAsMember),
RemoveMember(RemoveMember),
ExitMember(ExitMember),
SetTrust(SetTrustNodeId),
SetLimitedAgent(SetLimitedAgentId),
SetValidatorEmissionRate(SetValidatorEmissionRate),
SetMemberEncryptionKey(SetMemberEncKey),
SetTrustEncryptionKey(SetTrustEncKey),
SetPendingCreateRequestExpire(SetPendingCreateRequestExpire),
Send(Send),
CreateRequest(PendingCreateRequest),
CashConfirmation(CashConfirmation),
CreateCancellation(CreateCancellation),
RedeemCancellation(RedeemCancellation),
RedeemRequest(PendingRedeemRequest),
RedeemFulfillment(RedeemFulfillment),
AddAuthorityKey(AddAuthorityKey),
RemoveAuthorityKey(RemoveAuthorityKey),
AllowlistCidrBlock(AllowlistCidrBlock),
RemoveAllowlistCidrBlock(RemoveAllowlistCidrBlock),
RootAllowlistCidrBlock(RootAllowlistCidrBlock),
RootRemoveAllowlistCidrBlock(RootRemoveAllowlistCidrBlock),
SubmitProposal(SubmitProposal),
VoteProposal(VoteProposal),
RegisterSessionKeys(RegisterSessionKeys),
RuntimeUpgrade(RuntimeUpgrade),
WithdrawFromNetwork(WithdrawFromNetwork),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
pub struct WithdrawFromNetwork {}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisterSessionKeys {
pub block_production_pubkey: Address,
pub block_finalization_pubkey: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegisterAccountAsMember {
pub address: Address,
pub encryption_key: PublicKey,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveMember {
pub address: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExitMember {
pub address: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetTrustNodeId {
pub address: Address,
pub encryption_key: PublicKey,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetLimitedAgentId {
pub address: Option<Address>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetValidatorEmissionRate {
pub minor_units_per_emission: u64,
pub block_quota: u32,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetMemberEncKey {
pub key: PublicKey,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Blockstamp {
pub block_number: u64,
pub unix_timestamp_ms: i64,
pub is_stale: bool,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TotalIssuance {
pub total_issued: u64,
pub blockstamp: Blockstamp,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetTrustEncKey {
pub key: PublicKey,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SetPendingCreateRequestExpire {
pub expire_in_milliseconds: u64,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Send {
pub destination_account: Address,
pub amount_in_minor_unit: u64,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct BankAccountId {
pub routing_number: String,
pub account_number: String,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum BankAccountInfo {
Unencrypted(BankAccountId),
Encrypted(EncryptionError),
}
#[derive(Debug, Snafu, Serialize)]
pub enum BankAccountConversionErr {
#[snafu(display("Problem (de)serializing unencrypted bank info: {:?}", source))]
#[snafu(context(false))]
BadUnencryptedJson {
#[snafu(source(from(serde_json::Error, Box::new)))]
#[serde(serialize_with = "xand_utils::snafu_extensions::debug_serialize")]
source: Box<serde_json::Error>,
},
#[snafu(display("Encrypted bank info is too large"))]
EncryptedPayloadTooLarge,
#[snafu(display("Encrypted payload is invalid"))]
#[snafu(context(false))]
BadEncryptedPayload {
#[serde(serialize_with = "xand_utils::snafu_extensions::debug_serialize")]
source: Box<dyn std::error::Error + std::marker::Send + Sync>,
},
#[snafu(display("Failed to convert from bytes: {}", message))]
ByteConversionError { message: String },
}
impl From<BankAccountId> for BankAccountInfo {
fn from(id: BankAccountId) -> Self {
Self::Unencrypted(id)
}
}
pub struct ConversionFailure {
pub value: String,
pub original_type: &'static str,
pub target_type: &'static str,
}
pub fn convert_type<T, U>(v: T) -> Result<U, ConversionFailure>
where
T: Copy + Debug,
U: TryFrom<T>,
{
v.try_into().map_err(|_| ConversionFailure {
value: format!("{:?}", v),
original_type: type_name::<T>(),
target_type: type_name::<U>(),
})
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PendingCreateRequest {
pub amount_in_minor_unit: u64,
pub correlation_id: CorrelationId,
pub account: BankAccountInfo,
pub completing_transaction: Option<CreateRequestCompletion>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PendingRedeemRequest {
pub amount_in_minor_unit: u64,
pub correlation_id: CorrelationId,
pub account: BankAccountInfo,
pub completing_transaction: Option<RedeemRequestCompletion>,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RedeemFulfillment {
pub correlation_id: CorrelationId,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CashConfirmation {
pub correlation_id: CorrelationId,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateCancellation {
pub correlation_id: CorrelationId,
pub reason: CreateCancellationReason,
}
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
strum::Display,
strum::EnumString,
strum::IntoStaticStr,
)]
pub enum CreateCancellationReason {
Expired,
InvalidData,
BankNotFound,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RedeemCancellation {
pub correlation_id: CorrelationId,
pub reason: RedeemCancellationReason,
}
#[derive(
Clone,
Debug,
Hash,
PartialEq,
Eq,
Serialize,
Deserialize,
strum::Display,
strum::EnumString,
strum::IntoStaticStr,
)]
pub enum RedeemCancellationReason {
InvalidData,
AccountNotAllowed,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddAuthorityKey {
pub account_id: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveAuthorityKey {
pub account_id: Address,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AllowlistCidrBlock {
pub cidr_block: CidrBlock,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveAllowlistCidrBlock {
pub cidr_block: CidrBlock,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RootAllowlistCidrBlock {
pub account: Address,
pub cidr_block: CidrBlock,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RootRemoveAllowlistCidrBlock {
pub account: Address,
pub cidr_block: CidrBlock,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmitProposal {
pub proposed_action: AdministrativeTransaction,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VoteProposal {
pub id: u32,
pub vote: bool,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Constructor, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeUpgrade {
pub code: Vec<u8>,
pub xand_hash: XandHash,
pub wait_blocks: u32,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum CreateRequestCompletion {
Confirmation(TransactionId),
Cancellation(TransactionId),
Expiration(TransactionId),
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum RedeemRequestCompletion {
Confirmation(TransactionId),
Cancellation(TransactionId),
}
#[derive(Clone, Debug, Default)]
pub struct TransactionFilter {
pub addresses: Vec<Address>,
pub types: Vec<TransactionType>,
pub start_time: Option<DateTime<Utc>>,
pub end_time: Option<DateTime<Utc>>,
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum AdministrativeTransaction {
RegisterAccountAsMember(RegisterAccountAsMember),
SetTrust(SetTrustNodeId),
SetLimitedAgent(SetLimitedAgentId),
SetValidatorEmissionRate(SetValidatorEmissionRate),
AddAuthorityKey(AddAuthorityKey),
RemoveAuthorityKey(RemoveAuthorityKey),
RootAllowlistCidrBlock(RootAllowlistCidrBlock),
RootRemoveAllowlistCidrBlock(RootRemoveAllowlistCidrBlock),
RemoveMember(RemoveMember),
RuntimeUpgrade(RuntimeUpgrade),
}
impl AdministrativeTransaction {
pub fn from_transaction(t: XandTransaction) -> Option<Self> {
match t {
XandTransaction::RegisterMember(x) => {
Some(AdministrativeTransaction::RegisterAccountAsMember(x))
}
XandTransaction::RemoveMember(x) => Some(AdministrativeTransaction::RemoveMember(x)),
XandTransaction::SetTrust(x) => Some(AdministrativeTransaction::SetTrust(x)),
XandTransaction::SetLimitedAgent(x) => {
Some(AdministrativeTransaction::SetLimitedAgent(x))
}
XandTransaction::SetValidatorEmissionRate(x) => {
Some(AdministrativeTransaction::SetValidatorEmissionRate(x))
}
XandTransaction::AddAuthorityKey(x) => {
Some(AdministrativeTransaction::AddAuthorityKey(x))
}
XandTransaction::RemoveAuthorityKey(x) => {
Some(AdministrativeTransaction::RemoveAuthorityKey(x))
}
XandTransaction::AllowlistCidrBlock(_) => None,
XandTransaction::RemoveAllowlistCidrBlock(_) => None,
XandTransaction::RootAllowlistCidrBlock(x) => {
Some(AdministrativeTransaction::RootAllowlistCidrBlock(x))
}
XandTransaction::RootRemoveAllowlistCidrBlock(x) => {
Some(AdministrativeTransaction::RootRemoveAllowlistCidrBlock(x))
}
XandTransaction::RuntimeUpgrade(x) => {
Some(AdministrativeTransaction::RuntimeUpgrade(x))
}
XandTransaction::SubmitProposal(_) => None,
XandTransaction::VoteProposal(_) => None,
XandTransaction::SetMemberEncryptionKey(_) => None,
XandTransaction::SetTrustEncryptionKey(_) => None,
XandTransaction::SetPendingCreateRequestExpire(_) => None,
XandTransaction::Send(_) => None,
XandTransaction::CreateRequest(_) => None,
XandTransaction::CashConfirmation(_) => None,
XandTransaction::CreateCancellation(_) => None,
XandTransaction::RedeemCancellation(_) => None,
XandTransaction::RedeemRequest(_) => None,
XandTransaction::RedeemFulfillment(_) => None,
XandTransaction::RegisterSessionKeys(_) => None,
XandTransaction::ExitMember(_) => None,
XandTransaction::WithdrawFromNetwork(_) => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Proposal {
pub id: u32,
pub votes: HashMap<Address, bool>,
pub proposer: Address,
pub expiration_block_id: u32,
pub proposed_action: AdministrativeTransaction,
pub status: ProposalStage,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
feature = "serialization",
derive(serde::Deserialize, serde::Serialize)
)]
pub enum ProposalStage {
Proposed,
Accepted,
Rejected,
Invalid,
}
impl core::default::Default for ProposalStage {
fn default() -> Self {
ProposalStage::Proposed
}
}
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub struct ValidatorEmissionRate {
pub minor_units_per_emission: u64,
pub block_quota: u32,
}
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub struct GetValidatorEmissionProgress {
pub address: Address,
}
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub struct ValidatorEmissionProgress {
pub effective_emission_rate: ValidatorEmissionRate,
pub blocks_completed_progress: u32,
}
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub enum HealthStatus {
Unhealthy,
Healthy,
Syncing,
}
#[derive(Clone, Debug, Deserialize, Hash, Eq, PartialEq, Serialize)]
pub struct HealthResponse {
pub status: HealthStatus,
pub current_block: u64,
pub elapsed_blocks_since_startup: u64,
pub elapsed_time_since_startup_millis: i64,
pub best_known_block: u64,
}
#[cfg(test)]
mod test {
use super::*;
use chrono::Utc;
use std::str::FromStr;
use xand_address::Address;
fn example_addr() -> Address {
Address::from_str("5Hh9Gq21Ns4Knd6CjzjMymK6HeW9yYfxdMfhMoDyA8geHVbJ").unwrap()
}
fn example_encryption_key() -> PublicKey {
Default::default()
}
fn from_xand_txn(
id: TransactionId,
txn: XandTransaction,
signer: Address,
status: TransactionStatus,
) -> Transaction {
let timestamp = Utc.with_ymd_and_hms(2020, 2, 5, 6, 0, 0).unwrap();
Transaction::from_xand_txn(id, txn, signer, status, timestamp)
}
fn fake_account() -> BankAccountInfo {
let id = BankAccountId {
account_number: "123".to_string(),
routing_number: "456".to_string(),
};
id.into()
}
#[test]
pub fn transaction_size() {
assert!(std::mem::size_of::<Transaction>() < 300);
}
#[test]
pub fn transaction_status_from_str() {
let status_str = TransactionStatus::Unknown.to_string();
let result = TransactionStatus::from_str(&status_str).unwrap();
assert_eq!(result.to_string(), status_str);
}
#[test]
pub fn transaction_is_financial_event_create_request() {
let transaction = from_xand_txn(
TransactionId::default(),
XandTransaction::CreateRequest(PendingCreateRequest {
account: fake_account(),
amount_in_minor_unit: 42,
correlation_id: CorrelationId::gen_random(),
completing_transaction: None,
}),
example_addr(),
TransactionStatus::Pending,
);
assert!(transaction.is_financial_event());
}
#[test]
pub fn transaction_is_financial_event_cash_confirmation() {
let transaction = from_xand_txn(
TransactionId::from_str(
"0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
)
.unwrap(),
XandTransaction::CashConfirmation(CashConfirmation {
correlation_id: CorrelationId::gen_random(),
}),
example_addr(),
TransactionStatus::Committed,
);
assert!(transaction.is_financial_event());
}
#[test]
pub fn transaction_is_financial_event_redeem_request() {
let transaction = from_xand_txn(
TransactionId::default(),
XandTransaction::RedeemRequest(PendingRedeemRequest {
account: fake_account(),
amount_in_minor_unit: 42,
correlation_id: CorrelationId::gen_random(),
completing_transaction: None,
}),
example_addr(),
TransactionStatus::Committed,
);
assert!(transaction.is_financial_event());
}
#[test]
pub fn transaction_is_financial_event_redeem_fulfillment() {
let transaction = from_xand_txn(
TransactionId::from_str(
"0x0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
)
.unwrap(),
XandTransaction::RedeemFulfillment(RedeemFulfillment {
correlation_id: CorrelationId::gen_random(),
}),
example_addr(),
TransactionStatus::Committed,
);
assert!(transaction.is_financial_event());
}
#[test]
pub fn transaction_is_financial_event_send() {
let transaction = from_xand_txn(
TransactionId::default(),
XandTransaction::Send(Send {
amount_in_minor_unit: 42,
destination_account: example_addr(),
}),
example_addr(),
TransactionStatus::Pending,
);
assert!(transaction.is_financial_event());
}
#[test]
pub fn transaction_is_financial_event_some_non_financial() {
let transaction = from_xand_txn(
TransactionId::default(),
XandTransaction::RegisterMember(RegisterAccountAsMember {
address: example_addr(),
encryption_key: example_encryption_key(),
}),
example_addr(),
TransactionStatus::Pending,
);
assert!(!transaction.is_financial_event());
}
}