|
@@ -39,11 +39,11 @@ use std::collections::HashMap;
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
pub struct Transaction {
|
|
|
id: TransactionId,
|
|
|
- spend: Vec<Payment>,
|
|
|
+ spends: Vec<Payment>,
|
|
|
#[allow(dead_code)]
|
|
|
reference: String,
|
|
|
typ: Type,
|
|
|
- create: Vec<Payment>,
|
|
|
+ creates: Vec<Payment>,
|
|
|
status: Status,
|
|
|
#[serde(with = "ts_milliseconds")]
|
|
|
created_at: DateTime<Utc>,
|
|
@@ -57,9 +57,11 @@ impl Transaction {
|
|
|
status: Status,
|
|
|
spend: Vec<Payment>,
|
|
|
) -> Result<Transaction, Error> {
|
|
|
+ let created_at = Utc::now();
|
|
|
let id = Self::calculate_hash(
|
|
|
spend.iter().map(|t| &t.id).collect::<Vec<&PaymentId>>(),
|
|
|
vec![],
|
|
|
+ created_at,
|
|
|
)?;
|
|
|
let spend = spend
|
|
|
.into_iter()
|
|
@@ -70,12 +72,12 @@ impl Transaction {
|
|
|
.collect();
|
|
|
Ok(Self {
|
|
|
id,
|
|
|
- spend,
|
|
|
- create: vec![],
|
|
|
+ spends: spend,
|
|
|
+ creates: vec![],
|
|
|
typ: Type::Withdrawal,
|
|
|
reference,
|
|
|
status,
|
|
|
- created_at: Utc::now(),
|
|
|
+ created_at,
|
|
|
updated_at: Utc::now(),
|
|
|
})
|
|
|
}
|
|
@@ -85,12 +87,14 @@ impl Transaction {
|
|
|
status: Status,
|
|
|
pay_to: Vec<(AccountId, Amount)>,
|
|
|
) -> Result<Transaction, Error> {
|
|
|
+ let created_at = Utc::now();
|
|
|
let id = Self::calculate_hash(
|
|
|
vec![],
|
|
|
pay_to
|
|
|
.iter()
|
|
|
.map(|t| (&t.0, &t.1))
|
|
|
.collect::<Vec<(&AccountId, &Amount)>>(),
|
|
|
+ created_at,
|
|
|
)?;
|
|
|
let create = pay_to
|
|
|
.into_iter()
|
|
@@ -109,12 +113,12 @@ impl Transaction {
|
|
|
|
|
|
Ok(Self {
|
|
|
id,
|
|
|
- spend: vec![],
|
|
|
- create,
|
|
|
+ spends: vec![],
|
|
|
+ creates: create,
|
|
|
reference,
|
|
|
typ: Type::Deposit,
|
|
|
status,
|
|
|
- created_at: Utc::now(),
|
|
|
+ created_at,
|
|
|
updated_at: Utc::now(),
|
|
|
})
|
|
|
}
|
|
@@ -126,12 +130,14 @@ impl Transaction {
|
|
|
spend: Vec<Payment>,
|
|
|
pay_to: Vec<(AccountId, Amount)>,
|
|
|
) -> Result<Transaction, Error> {
|
|
|
+ let created_at = Utc::now();
|
|
|
let id = Self::calculate_hash(
|
|
|
spend.iter().map(|t| &t.id).collect::<Vec<&PaymentId>>(),
|
|
|
pay_to
|
|
|
.iter()
|
|
|
.map(|t| (&t.0, &t.1))
|
|
|
.collect::<Vec<(&AccountId, &Amount)>>(),
|
|
|
+ created_at,
|
|
|
)?;
|
|
|
|
|
|
for (i, input) in spend.iter().enumerate() {
|
|
@@ -165,11 +171,11 @@ impl Transaction {
|
|
|
Ok(Self {
|
|
|
id,
|
|
|
reference,
|
|
|
- spend,
|
|
|
+ spends: spend,
|
|
|
typ,
|
|
|
- create,
|
|
|
+ creates: create,
|
|
|
status,
|
|
|
- created_at: Utc::now(),
|
|
|
+ created_at,
|
|
|
updated_at: Utc::now(),
|
|
|
})
|
|
|
}
|
|
@@ -177,8 +183,13 @@ impl Transaction {
|
|
|
fn calculate_hash(
|
|
|
spend: Vec<&PaymentId>,
|
|
|
create: Vec<(&AccountId, &Amount)>,
|
|
|
+ created_at: DateTime<Utc>,
|
|
|
) -> Result<TransactionId, Error> {
|
|
|
let mut hasher = Sha256::new();
|
|
|
+ let mut spend = spend;
|
|
|
+
|
|
|
+ spend.sort();
|
|
|
+
|
|
|
for id in spend.into_iter() {
|
|
|
hasher.update(&bincode::serialize(id)?);
|
|
|
}
|
|
@@ -186,6 +197,7 @@ impl Transaction {
|
|
|
hasher.update(&bincode::serialize(account)?);
|
|
|
hasher.update(&bincode::serialize(amount)?);
|
|
|
}
|
|
|
+ hasher.update(&created_at.timestamp_millis().to_le_bytes());
|
|
|
Ok(TransactionId::new(hasher.finalize().into()))
|
|
|
}
|
|
|
|
|
@@ -201,13 +213,13 @@ impl Transaction {
|
|
|
#[inline]
|
|
|
pub fn change_status(&mut self, new_status: Status) -> Result<(), Error> {
|
|
|
if self.status.can_transition_to(&new_status) {
|
|
|
- self.spend.iter_mut().for_each(|payment| {
|
|
|
+ self.spends.iter_mut().for_each(|payment| {
|
|
|
payment.status = new_status.clone();
|
|
|
if new_status.is_rollback() {
|
|
|
payment.spent_by = None;
|
|
|
}
|
|
|
});
|
|
|
- self.create.iter_mut().for_each(|payment| {
|
|
|
+ self.creates.iter_mut().for_each(|payment| {
|
|
|
payment.status = new_status.clone();
|
|
|
});
|
|
|
self.status = new_status;
|
|
@@ -239,11 +251,12 @@ impl Transaction {
|
|
|
|
|
|
pub(crate) fn validate(&self) -> Result<(), Error> {
|
|
|
let calculated_id = Self::calculate_hash(
|
|
|
- self.spend.iter().map(|p| &p.id).collect::<Vec<_>>(),
|
|
|
- self.create
|
|
|
+ self.spends.iter().map(|p| &p.id).collect::<Vec<_>>(),
|
|
|
+ self.creates
|
|
|
.iter()
|
|
|
.map(|p| (&p.to, &p.amount))
|
|
|
.collect::<Vec<_>>(),
|
|
|
+ self.created_at,
|
|
|
)?;
|
|
|
|
|
|
if calculated_id != self.id {
|
|
@@ -253,7 +266,7 @@ impl Transaction {
|
|
|
let mut debit = HashMap::<Asset, AmountCents>::new();
|
|
|
let mut credit = HashMap::<Asset, AmountCents>::new();
|
|
|
|
|
|
- for (i, input) in self.spend.iter().enumerate() {
|
|
|
+ for (i, input) in self.spends.iter().enumerate() {
|
|
|
if input.spent_by.is_some() && input.spent_by.as_ref() != Some(&self.id) {
|
|
|
return Err(Error::SpentPayment(i));
|
|
|
}
|
|
@@ -276,7 +289,7 @@ impl Transaction {
|
|
|
return Ok(());
|
|
|
}
|
|
|
|
|
|
- for (i, output) in self.create.iter().enumerate() {
|
|
|
+ for (i, output) in self.creates.iter().enumerate() {
|
|
|
if output.spent_by.is_some() {
|
|
|
return Err(Error::SpentPayment(i));
|
|
|
}
|
|
@@ -311,12 +324,12 @@ impl Transaction {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
- pub fn spent(&self) -> &[Payment] {
|
|
|
- &self.spend
|
|
|
+ pub fn spends(&self) -> &[Payment] {
|
|
|
+ &self.spends
|
|
|
}
|
|
|
|
|
|
- pub fn created(&self) -> &[Payment] {
|
|
|
- &self.create
|
|
|
+ pub fn creates(&self) -> &[Payment] {
|
|
|
+ &self.creates
|
|
|
}
|
|
|
|
|
|
pub fn id(&self) -> &TransactionId {
|
|
@@ -357,13 +370,13 @@ impl Transaction {
|
|
|
self.validate()?;
|
|
|
self.updated_at = Utc::now();
|
|
|
batch.store_transaction(self).await?;
|
|
|
- for payment in self.create.iter() {
|
|
|
+ for payment in self.creates.iter() {
|
|
|
batch.store_new_payment(payment).await?;
|
|
|
batch
|
|
|
.relate_account_to_transaction(&self, &payment.to)
|
|
|
.await?;
|
|
|
}
|
|
|
- for input in self.spend.iter() {
|
|
|
+ for input in self.spends.iter() {
|
|
|
batch
|
|
|
.spend_payment(&input.id, self.status.clone(), &self.id)
|
|
|
.await?;
|
|
@@ -383,8 +396,8 @@ impl TryFrom<from_db::Transaction> for Transaction {
|
|
|
let tx = Transaction {
|
|
|
id: value.id,
|
|
|
typ: value.typ,
|
|
|
- spend: value.spend,
|
|
|
- create: value.create,
|
|
|
+ spends: value.spend,
|
|
|
+ creates: value.create,
|
|
|
reference: value.reference,
|
|
|
status: value.status,
|
|
|
created_at: value.created_at,
|