mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Ensure the transaction-chaining scheduler doesn't accumulate the same output multiple times
This commit is contained in:
@@ -13,6 +13,7 @@ create_db! {
|
|||||||
TransactionChainingScheduler {
|
TransactionChainingScheduler {
|
||||||
OperatingCosts: (coin: Coin) -> Amount,
|
OperatingCosts: (coin: Coin) -> Amount,
|
||||||
SerializedOutputs: (key: &[u8], coin: Coin) -> Vec<u8>,
|
SerializedOutputs: (key: &[u8], coin: Coin) -> Vec<u8>,
|
||||||
|
AlreadyAccumulatedOutput: (id: &[u8]) -> (),
|
||||||
// We should be immediately able to schedule the fulfillment of payments, yet this may not be
|
// We should be immediately able to schedule the fulfillment of payments, yet this may not be
|
||||||
// possible if we're in the middle of a multisig rotation (as our output set will be split)
|
// possible if we're in the middle of a multisig rotation (as our output set will be split)
|
||||||
SerializedQueuedPayments: (key: &[u8], coin: Coin) -> Vec<u8>,
|
SerializedQueuedPayments: (key: &[u8], coin: Coin) -> Vec<u8>,
|
||||||
@@ -58,6 +59,21 @@ impl<S: ScannerFeed> Db<S> {
|
|||||||
SerializedOutputs::del(txn, key.to_bytes().as_ref(), coin);
|
SerializedOutputs::del(txn, key.to_bytes().as_ref(), coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_already_accumulated_output(
|
||||||
|
txn: &mut impl DbTxn,
|
||||||
|
output: <OutputFor<S> as ReceivedOutput<KeyFor<S>, AddressFor<S>>>::Id,
|
||||||
|
) {
|
||||||
|
AlreadyAccumulatedOutput::set(txn, output.as_ref(), &());
|
||||||
|
}
|
||||||
|
pub(crate) fn take_if_already_accumulated_output(
|
||||||
|
txn: &mut impl DbTxn,
|
||||||
|
output: <OutputFor<S> as ReceivedOutput<KeyFor<S>, AddressFor<S>>>::Id,
|
||||||
|
) -> bool {
|
||||||
|
let res = AlreadyAccumulatedOutput::get(txn, output.as_ref()).is_some();
|
||||||
|
AlreadyAccumulatedOutput::del(txn, output.as_ref());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn queued_payments(
|
pub(crate) fn queued_payments(
|
||||||
getter: &impl Get,
|
getter: &impl Get,
|
||||||
key: KeyFor<S>,
|
key: KeyFor<S>,
|
||||||
|
|||||||
@@ -60,7 +60,9 @@ impl<S: ScannerFeed, P: TransactionPlanner<S, EffectedReceivedOutputs<S>>> Sched
|
|||||||
// that'd risk underflow
|
// that'd risk underflow
|
||||||
let available =
|
let available =
|
||||||
operating_costs + outputs.iter().map(|output| output.balance().amount.0).sum::<u64>();
|
operating_costs + outputs.iter().map(|output| output.balance().amount.0).sum::<u64>();
|
||||||
assert!(available >= payments.iter().map(|payment| payment.balance().amount.0).sum::<u64>());
|
assert!(
|
||||||
|
available >= payments.iter().map(|payment| payment.balance().amount.0).sum::<u64>()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let amount_of_payments_that_can_be_handled =
|
let amount_of_payments_that_can_be_handled =
|
||||||
@@ -179,6 +181,9 @@ impl<S: ScannerFeed, P: TransactionPlanner<S, EffectedReceivedOutputs<S>>> Sched
|
|||||||
// Only handle Change so if someone burns to an External address, we don't use it here
|
// Only handle Change so if someone burns to an External address, we don't use it here
|
||||||
// when the scanner will tell us to return it (without accumulating it)
|
// when the scanner will tell us to return it (without accumulating it)
|
||||||
effected_received_outputs.retain(|output| output.kind() == OutputType::Change);
|
effected_received_outputs.retain(|output| output.kind() == OutputType::Change);
|
||||||
|
for output in &effected_received_outputs {
|
||||||
|
Db::<S>::set_already_accumulated_output(txn, output.id());
|
||||||
|
}
|
||||||
outputs.append(&mut effected_received_outputs);
|
outputs.append(&mut effected_received_outputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,9 +241,13 @@ impl<S: ScannerFeed, P: TransactionPlanner<S, EffectedReceivedOutputs<S>>> Sched
|
|||||||
let mut outputs_by_coin = HashMap::with_capacity(1);
|
let mut outputs_by_coin = HashMap::with_capacity(1);
|
||||||
for output in update.outputs().iter().filter(|output| output.key() == *key) {
|
for output in update.outputs().iter().filter(|output| output.key() == *key) {
|
||||||
match output.kind() {
|
match output.kind() {
|
||||||
OutputType::External | OutputType::Forwarded => {},
|
OutputType::External | OutputType::Forwarded => {}
|
||||||
// TODO: Only accumulate these if we haven't already, but do accumulate if not
|
// Only accumulate these if we haven't already
|
||||||
OutputType::Branch | OutputType::Change => todo!("TODO"),
|
OutputType::Branch | OutputType::Change => {
|
||||||
|
if Db::<S>::take_if_already_accumulated_output(txn, output.id()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let coin = output.balance().coin;
|
let coin = output.balance().coin;
|
||||||
if let std::collections::hash_map::Entry::Vacant(e) = outputs_by_coin.entry(coin) {
|
if let std::collections::hash_map::Entry::Vacant(e) = outputs_by_coin.entry(coin) {
|
||||||
|
|||||||
Reference in New Issue
Block a user