Skip to content

Commit

Permalink
fee-sweeper: Ingest latest changes from base repo
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Jun 28, 2024
1 parent b6c0e99 commit edfcd14
Show file tree
Hide file tree
Showing 12 changed files with 1,203 additions and 4 deletions.
7 changes: 6 additions & 1 deletion dealer/Cargo.toml → Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
[workspace]
members = ["renegade-dealer", "renegade-dealer-api"]
members = [
"dealer/renegade-dealer",
"dealer/renegade-dealer-api",
"fee-sweeper",
"price-reporter",
]

[profile.bench]
opt-level = 3
Expand Down
4 changes: 2 additions & 2 deletions fee-sweeper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ tokio = { version = "1.10", features = ["full"] }
# === Infra === #
aws-sdk-secretsmanager = "1.37"
aws-config = "1.5"
diesel = { version = "2.1", features = ["postgres", "numeric", "uuid"] }
diesel = { version = "2.2", features = ["postgres", "numeric", "uuid"] }

# === Blockchain Interaction === #
alloy-sol-types = "0.3.1"
Expand All @@ -31,7 +31,7 @@ renegade-util = { package = "util", git = "https://github.com/renegade-fi/renega

# === Misc Dependencies === #
base64 = "0.22"
bigdecimal = { version = "0.3", features = ["serde"] }
bigdecimal = { version = "0.4", features = ["serde"] }
futures = "0.3"
http = "1.1"
num-bigint = "0.4"
Expand Down
5 changes: 5 additions & 0 deletions fee-sweeper/src/src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//! Database code

pub mod models;
#[allow(missing_docs)]
pub mod schema;
88 changes: 88 additions & 0 deletions fee-sweeper/src/src/db/models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#![allow(missing_docs)]
#![allow(trivial_bounds)]

use bigdecimal::BigDecimal;
use diesel::prelude::*;
use num_bigint::BigInt;
use renegade_circuit_types::note::Note;
use renegade_crypto::fields::scalar_to_bigint;
use renegade_util::hex::{biguint_to_hex_addr, jubjub_to_hex_string};
use uuid::Uuid;

use crate::db::schema::fees;

/// A fee that has been indexed by the indexer
#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::db::schema::fees)]
#[diesel(check_for_backend(diesel::pg::Pg))]
#[allow(missing_docs, clippy::missing_docs_in_private_items)]
pub struct Fee {
pub id: i32,
pub tx_hash: String,
pub mint: String,
pub amount: BigDecimal,
pub blinder: BigDecimal,
pub receiver: String,
pub redeemed: bool,
}

/// A new fee inserted into the database
#[derive(Insertable)]
#[diesel(table_name = fees)]
pub struct NewFee {
pub tx_hash: String,
pub mint: String,
pub amount: BigDecimal,
pub blinder: BigDecimal,
pub receiver: String,
}

impl NewFee {
/// Construct a fee from a note
pub fn new_from_note(note: &Note, tx_hash: String) -> Self {
let mint = biguint_to_hex_addr(&note.mint);
let amount = BigInt::from(note.amount).into();
let blinder = scalar_to_bigint(&note.blinder).into();
let receiver = jubjub_to_hex_string(&note.receiver);

NewFee {
tx_hash,
mint,
amount,
blinder,
receiver,
}
}
}

/// Metadata information maintained by the indexer
#[derive(Clone, Queryable, Selectable)]
#[diesel(table_name = crate::db::schema::indexing_metadata)]
#[diesel(check_for_backend(diesel::pg::Pg))]
#[allow(missing_docs, clippy::missing_docs_in_private_items)]
pub struct Metadata {
pub key: String,
pub value: String,
}

/// A metadata entry for a wallet managed by the indexer
#[derive(Clone, Queryable, Selectable, Insertable)]
#[diesel(table_name = crate::db::schema::wallets)]
#[diesel(check_for_backend(diesel::pg::Pg))]
#[allow(missing_docs, clippy::missing_docs_in_private_items)]
pub struct WalletMetadata {
pub id: Uuid,
pub mints: Vec<Option<String>>,
pub secret_id: String,
}

impl WalletMetadata {
/// Construct a new wallet metadata entry
pub fn empty(id: Uuid, secret_id: String) -> Self {
WalletMetadata {
id,
mints: vec![],
secret_id,
}
}
}
34 changes: 34 additions & 0 deletions fee-sweeper/src/src/db/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @generated automatically by Diesel CLI.

diesel::table! {
fees (id) {
id -> Int4,
tx_hash -> Text,
mint -> Text,
amount -> Numeric,
blinder -> Numeric,
receiver -> Text,
redeemed -> Bool,
}
}

diesel::table! {
indexing_metadata (key) {
key -> Text,
value -> Text,
}
}

diesel::table! {
wallets (id) {
id -> Uuid,
mints -> Array<Nullable<Text>>,
secret_id -> Text,
}
}

diesel::allow_tables_to_appear_in_same_query!(
fees,
indexing_metadata,
wallets,
);
124 changes: 124 additions & 0 deletions fee-sweeper/src/src/indexer/index_fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//! Phase one of the sweeper's execution; index all fees since the last consistent block

use alloy_sol_types::SolCall;
use arbitrum_client::abi::settleOfflineFeeCall;
use arbitrum_client::{
abi::NotePostedFilter, constants::SELECTOR_LEN,
helpers::parse_note_ciphertext_from_settle_offline_fee,
};
use ethers::contract::LogMeta;
use ethers::middleware::Middleware;
use ethers::types::TxHash;
use renegade_circuit_types::elgamal::ElGamalCiphertext;
use renegade_circuit_types::native_helpers::elgamal_decrypt;
use renegade_circuit_types::note::{Note, NOTE_CIPHERTEXT_SIZE};
use renegade_circuit_types::wallet::NoteCommitment;
use renegade_constants::Scalar;
use renegade_crypto::fields::{scalar_to_biguint, scalar_to_u128, u256_to_scalar};
use renegade_util::raw_err_str;
use tracing::info;

use crate::db::models::NewFee;
use crate::Indexer;

impl Indexer {
/// Index all fees since the given block
pub async fn index_fees(&mut self) -> Result<(), String> {
let block_number = self.get_latest_block()?;
info!("indexing fees from block {block_number}");

let filter = self
.arbitrum_client
.get_darkpool_client()
.event::<NotePostedFilter>()
.from_block(block_number);

let events = filter
.query_with_meta()
.await
.map_err(raw_err_str!("failed to create note posted stream: {}"))?;

let mut most_recent_block = block_number;
for (event, meta) in events {
let block = meta.block_number.as_u64();
let note_comm = u256_to_scalar(&event.note_commitment);
self.index_note(note_comm, meta).await?;

if block > most_recent_block {
most_recent_block = block;
self.update_latest_block(most_recent_block)?;
}
}

Ok(())
}

/// Index a note
async fn index_note(&mut self, note_comm: NoteCommitment, meta: LogMeta) -> Result<(), String> {
let note = self.get_note_from_tx(meta.transaction_hash).await?;
let tx = format!("{:#x}", meta.transaction_hash);
if note.commitment() != note_comm {
info!("not receiver, skipping");
return Ok(());
} else {
info!("indexing note from tx: {tx}");
}

// Check that the note's nullifier has not been spent
let nullifier = note.nullifier();
if self
.arbitrum_client
.check_nullifier_used(nullifier)
.await
.map_err(raw_err_str!("failed to check nullifier: {}"))?
{
info!("note nullifier already spent, skipping");
return Ok(());
}

// Otherwise, index the note
let fee = NewFee::new_from_note(&note, tx);
self.insert_fee(fee)
}

/// Get a note from a transaction body
pub(crate) async fn get_note_from_tx(&self, tx_hash: TxHash) -> Result<Note, String> {
// Parse the note from the tx
let tx = self
.arbitrum_client
.get_darkpool_client()
.client()
.get_transaction(tx_hash)
.await
.map_err(raw_err_str!("failed to query tx: {}"))?
.ok_or_else(|| format!("tx not found: {}", tx_hash))?;

let calldata: Vec<u8> = tx.input.to_vec();
let selector: [u8; 4] = calldata[..SELECTOR_LEN].try_into().unwrap();
let encryption = match selector {
<settleOfflineFeeCall as SolCall>::SELECTOR => {
parse_note_ciphertext_from_settle_offline_fee(&calldata)
.map_err(raw_err_str!("failed to parse ciphertext: {}"))?
}
sel => return Err(format!("invalid selector when parsing note: {sel:?}")),
};

// Decrypt the note
let note = self.decrypt_note(&encryption);
Ok(note)
}

/// Decrypt a note using the decryption key
fn decrypt_note(&self, note: &ElGamalCiphertext<NOTE_CIPHERTEXT_SIZE>) -> Note {
// The ciphertext stores all note values except the encryption key
let cleartext_values: [Scalar; NOTE_CIPHERTEXT_SIZE] =
elgamal_decrypt(note, &self.decryption_key);

Note {
mint: scalar_to_biguint(&cleartext_values[0]),
amount: scalar_to_u128(&cleartext_values[1]),
receiver: self.decryption_key.public_key(),
blinder: cleartext_values[2],
}
}
}
53 changes: 53 additions & 0 deletions fee-sweeper/src/src/indexer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! The indexer handles the indexing and redemption of fee notes

use arbitrum_client::{client::ArbitrumClient, constants::Chain};
use aws_config::SdkConfig as AwsConfig;
use diesel::PgConnection;
use renegade_circuit_types::elgamal::DecryptionKey;

use crate::relayer_client::RelayerClient;

pub mod index_fees;
pub mod queries;
pub mod redeem_fees;

/// Stores the dependencies needed to index the chain
pub(crate) struct Indexer {
/// The id of the chain this indexer targets
pub chain_id: u64,
/// The chain this indexer targets
pub chain: Chain,
/// A client for interacting with the relayer
pub relayer_client: RelayerClient,
/// The Arbitrum client
pub arbitrum_client: ArbitrumClient,
/// The decryption key
pub decryption_key: DecryptionKey,
/// A connection to the DB
pub db_conn: PgConnection,
/// The AWS config
pub aws_config: AwsConfig,
}

impl Indexer {
/// Constructor
pub fn new(
chain_id: u64,
chain: Chain,
aws_config: AwsConfig,
arbitrum_client: ArbitrumClient,
decryption_key: DecryptionKey,
db_conn: PgConnection,
relayer_client: RelayerClient,
) -> Self {
Indexer {
chain_id,
chain,
arbitrum_client,
decryption_key,
db_conn,
relayer_client,
aws_config,
}
}
}
Loading

0 comments on commit edfcd14

Please sign in to comment.