Skip to content

Commit

Permalink
Lock free payment validator refactor
Browse files Browse the repository at this point in the history
This patch contains a significant number of changes to payment validator
and in fact a complete re-design of how it moves information around,
although the core logic itself remains the same.

Instead of having payment validator state stored in a lazy static and
then accessed by a lock from various places, it's now stored in a single
scope, the rita fast loop.

This dramatically simplifies the way information flows through the
program, prviously when a payment was made by payment controller the
entire state of payment validator was accessed via a lock and updated,
now that data is simply passed as a function argument.

The one case where cross thread interaction is required is passing
information from the actix endpoints into the rita client thread.
Instead of using a lock over all of the payment validator state this has
now been replaced with a lock free queue that payment validator dequeues
from during validation.

This did require two changes to how Rita behaves.

Payments that are not yet validated are no longer included in the rita exit debts
endpoint. We made that change to prevent clients from paying the exit
twice if it was running slowly. The lock free changes make this slow
operation much less probable and also repeated payments are a symptom of
the low timeout bug we have just recently encountered and fixed.

Second when a client sent in a transaction that was a duplicate we would
return an error, we no longer do so. Instead we return ok and drop the
tx as a duplicate later.

Both of these changes required access to the entire program state in
random other threads and contributed to slower operation and deadlocks.

Ideally I'd like this to be the model for how to refactor other Rita
modules going forward. There's a lot of cruft still left over from the
original Actix actors structure, which we essentially replicated as
closely as possible when moving to individual threads in Async await.

Now we have to do the second part of that refinement.

As a final note Althea paymetns have been modified to expect a
MsgMicroTX and payment_controller needs to be updated to send one.
  • Loading branch information
jkilpatr committed Mar 29, 2024
1 parent 51836d3 commit 6f1149e
Show file tree
Hide file tree
Showing 10 changed files with 755 additions and 808 deletions.
55 changes: 49 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ deep_space = {version = "2", features = ["althea"], default-features=false}
web30 = "1.2"
clarity = "1.3"
awc = {version = "3.2", default-features = false, features=["openssl", "compress-gzip", "compress-zstd"]}
althea_proto = "0.6"
16 changes: 0 additions & 16 deletions althea_types/src/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use std::fmt;
use std::fmt::Display;
use std::hash::{Hash, Hasher};
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::str::FromStr;
use std::time::{Duration, SystemTime};

Expand Down Expand Up @@ -389,21 +388,6 @@ pub struct LocalIdentity {
pub global: Identity,
}

/// This is all the data a light client needs to open a light client tunnel
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Copy)]
pub struct LightClientLocalIdentity {
pub wg_port: u16,
/// If we have an existing tunnel, None if we don't know
pub have_tunnel: Option<bool>,
pub global: Identity,
/// we have to replicate dhcp ourselves due to the android vpn api
pub tunnel_address: Ipv4Addr,
/// the local_fee of the node passing light client traffic, much bigger
/// than the actual babel price field for ergonomics around downcasting
/// the number after upcasting when we compute it.
pub price: u128,
}

/// This represents a generic payment that may be to or from us
/// it contains a txid from a published transaction
/// that should be validated against the blockchain
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ awc = {workspace = true}
actix-rt = "2.8"
deep_space = {workspace = true}
clarity = {workspace = true}
althea_proto = "0.6.0"
althea_proto = {workspace = true}
futures = { version = "0.3", features = ["compat"] }
num256 = "0.5"
num-traits="0.2"
Expand Down
2 changes: 2 additions & 0 deletions rita_common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ althea_types = { path = "../althea_types" }
deep_space = {workspace = true}
prost-types ="0.12"
cosmos-sdk-proto-althea = {package = "cosmos-sdk-proto-althea", version = "0.16", features = ["ethermint"]}
althea_proto = {workspace = true}
crossbeam = "0.8"

[dependencies.regex]
version = "1.6"
Expand Down
33 changes: 11 additions & 22 deletions rita_common/src/network_endpoints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Network endptoints for common Rita functionality (such as exchanging hello messages)
use crate::payment_validator::{validate_later, ToValidate};
use crate::payment_validator::{add_to_incoming_transaction_queue, ToValidate};
use crate::peer_listener::structs::Peer;
use crate::tm_identity_callback;
use crate::tunnel_manager::id_callback::IdentityCallback;
use crate::{tm_identity_callback, RitaCommonError};

use actix_web_async::http::StatusCode;
use actix_web_async::web::Json;
Expand All @@ -20,40 +20,29 @@ pub async fn make_payments(item: Json<PaymentTx>) -> HttpResponse {
let ts = ToValidate {
payment: pmt,
received: Instant::now(),
checked: false,
};
add_to_incoming_transaction_queue(ts);

match validate_later(ts) {
Ok(()) | Err(RitaCommonError::DuplicatePayment) => {
HttpResponse::Ok().json("Payment Received!")
}
Err(e) => HttpResponse::build(StatusCode::from_u16(400u16).unwrap()).json(&format!("{e}")),
}
// we can't actually check validity here so we simply return Ok
// in any case it's not possible to return if the payment was invalid
// as we may find a validation condition only after making many other checks
HttpResponse::Ok().json("Payment Received!")
}

/// The recieve side of the make payments v2 call. This processes a list of payments instead of a single payment
pub async fn make_payments_v2(item: Json<HashSet<PaymentTx>>) -> HttpResponse {
let pmt_list = item.into_inner();
let mut build_err = String::new();
for pmt in pmt_list {
let ts = ToValidate {
payment: pmt,
received: Instant::now(),
checked: false,
};

// Duplicates will be removed here
match validate_later(ts) {
Ok(()) | Err(RitaCommonError::DuplicatePayment) => {}
Err(e) => {
build_err.push_str(&format!("{e}\n"));
}
}
add_to_incoming_transaction_queue(ts);
}

if !build_err.is_empty() {
return HttpResponse::build(StatusCode::from_u16(400u16).unwrap()).json(&build_err);
}
// we can't actually check validity here so we simply return Ok
// in any case it's not possible to return if the payment was invalid
// as we may find a validation condition only after making many other checks
HttpResponse::Ok().json("Payment Received!")
}

Expand Down
Loading

0 comments on commit 6f1149e

Please sign in to comment.