Skip to content

Commit

Permalink
Merge pull request #34 from worldcoin/optional-binaries
Browse files Browse the repository at this point in the history
Handle optional binaries with transaction request
  • Loading branch information
cichaczem authored May 17, 2024
2 parents fae0ba9 + 564e234 commit bfed77d
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 7 deletions.
1 change: 1 addition & 0 deletions db/migrations/004_transactions_binaries.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE transactions ADD COLUMN blobs BYTEA[]
18 changes: 11 additions & 7 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ impl Database {
value: U256,
gas_limit: U256,
priority: TransactionPriority,
blobs: Option<Vec<Vec<u8>>>,
relayer_id: &str,
) -> eyre::Result<()> {
let mut tx = self.pool.begin().await?;
Expand All @@ -290,8 +291,8 @@ impl Database {

sqlx::query(
r#"
INSERT INTO transactions (id, tx_to, data, value, gas_limit, priority, relayer_id, nonce)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
INSERT INTO transactions (id, tx_to, data, value, gas_limit, priority, relayer_id, nonce, blobs)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
"#,
)
.bind(tx_id)
Expand All @@ -302,6 +303,7 @@ impl Database {
.bind(priority)
.bind(relayer_id)
.bind(nonce)
.bind(blobs)
.execute(tx.as_mut())
.await?;

Expand All @@ -314,7 +316,7 @@ impl Database {
pub async fn get_unsent_txs(&self) -> eyre::Result<Vec<UnsentTx>> {
Ok(sqlx::query_as(
r#"
SELECT r.id as relayer_id, t.id, t.tx_to, t.data, t.value, t.gas_limit, t.priority, t.nonce, r.key_id, r.chain_id
SELECT r.id as relayer_id, t.id, t.tx_to, t.data, t.value, t.gas_limit, t.priority, t.nonce, t.blobs, r.key_id, r.chain_id
FROM transactions t
LEFT JOIN sent_transactions s ON (t.id = s.tx_id)
INNER JOIN relayers r ON (t.relayer_id = r.id)
Expand Down Expand Up @@ -765,7 +767,7 @@ impl Database {
Ok(sqlx::query_as(
r#"
SELECT r.id as relayer_id, t.id, t.tx_to, t.data, t.value, t.gas_limit, t.nonce,
r.key_id, r.chain_id,
t.blobs, r.key_id, r.chain_id,
s.initial_max_fee_per_gas, s.initial_max_priority_fee_per_gas, s.escalation_count
FROM transactions t
JOIN sent_transactions s ON t.id = s.tx_id
Expand Down Expand Up @@ -849,7 +851,7 @@ impl Database {
Ok(sqlx::query_as(
r#"
SELECT t.id as tx_id, t.tx_to as to, t.data, t.value, t.gas_limit, t.nonce,
h.tx_hash, s.status
t.blobs, h.tx_hash, s.status
FROM transactions t
LEFT JOIN sent_transactions s ON t.id = s.tx_id
LEFT JOIN tx_hashes h ON s.valid_tx_hash = h.tx_hash
Expand All @@ -875,7 +877,7 @@ impl Database {
Ok(sqlx::query_as(
r#"
SELECT t.id as tx_id, t.tx_to as to, t.data, t.value, t.gas_limit, t.nonce,
h.tx_hash, s.status
t. blobs, h.tx_hash, s.status
FROM transactions t
LEFT JOIN sent_transactions s ON t.id = s.tx_id
LEFT JOIN tx_hashes h ON s.valid_tx_hash = h.tx_hash
Expand Down Expand Up @@ -1410,12 +1412,13 @@ mod tests {
let value = U256::from(0);
let gas_limit = U256::from(0);
let priority = TransactionPriority::Regular;
let blobs = None;

let tx = db.read_tx(tx_id).await?;
assert!(tx.is_none(), "Tx has not been sent yet");

db.create_transaction(
tx_id, to, data, value, gas_limit, priority, relayer_id,
tx_id, to, data, value, gas_limit, priority, blobs, relayer_id,
)
.await?;

Expand All @@ -1428,6 +1431,7 @@ mod tests {
assert_eq!(tx.gas_limit.0, gas_limit);
assert_eq!(tx.nonce, 0);
assert_eq!(tx.tx_hash, None);
assert_eq!(tx.blobs, None);

let unsent_txs = db.read_txs(relayer_id, None).await?;
assert_eq!(unsent_txs.len(), 1, "1 unsent tx");
Expand Down
3 changes: 3 additions & 0 deletions src/db/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub struct UnsentTx {
pub priority: TransactionPriority,
#[sqlx(try_from = "i64")]
pub nonce: u64,
pub blobs: Option<Vec<Vec<u8>>>,
pub key_id: String,
#[sqlx(try_from = "i64")]
pub chain_id: u64,
Expand All @@ -34,6 +35,7 @@ pub struct TxForEscalation {
pub gas_limit: U256Wrapper,
#[sqlx(try_from = "i64")]
pub nonce: u64,
pub blobs: Option<Vec<Vec<u8>>>,
pub key_id: String,
#[sqlx(try_from = "i64")]
pub chain_id: u64,
Expand All @@ -52,6 +54,7 @@ pub struct ReadTxData {
pub gas_limit: U256Wrapper,
#[sqlx(try_from = "i64")]
pub nonce: u64,
pub blobs: Option<Vec<Vec<u8>>>,

// Sent tx data
pub tx_hash: Option<H256Wrapper>,
Expand Down
1 change: 1 addition & 0 deletions src/serde_utils.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod base64_binary;
pub mod decimal_u256;
85 changes: 85 additions & 0 deletions src/serde_utils/base64_binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use base64::engine::general_purpose;
use base64::Engine as _;
use serde::Deserialize;

pub fn serialize<S>(
blobs: &Option<Vec<Vec<u8>>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match blobs {
Some(blobs) => {
let base64_vec: Vec<String> = blobs
.iter()
.map(|binary| general_purpose::STANDARD.encode(binary))
.collect();

serializer.serialize_some(&base64_vec)
}
None => serializer.serialize_none(),
}
}

pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<Vec<Vec<u8>>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let base64_strings: Option<Vec<String>> =
Option::deserialize(deserializer)?;

match base64_strings {
Some(base64_vec) => {
let decoded_vec: Result<Vec<Vec<u8>>, _> = base64_vec
.into_iter()
.map(|base64_str| {
general_purpose::STANDARD
.decode(base64_str)
.map_err(serde::de::Error::custom)
})
.collect();

Ok(Some(decoded_vec?))
}
None => Ok(None),
}
}

#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use serde_json;

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Test {
#[serde(with = "super")]
blobs: Option<Vec<Vec<u8>>>,
}

#[test]
fn test_deserialize_with_valid_input() {
let blobs =
Some(["Hello", "world!"].map(|b| b.as_bytes().to_vec()).to_vec());
let test = Test {
blobs: blobs.clone(),
};

let s = serde_json::to_string(&test).unwrap();

let test: Test = serde_json::from_str(&s).unwrap();
assert_eq!(test.blobs, blobs);
}

#[test]
fn test_deserialize_with_null_input() {
let test = Test { blobs: None };

let s = serde_json::to_string(&test).unwrap();

let test: Test = serde_json::from_str(&s).unwrap();
assert_eq!(test.blobs, None);
}
}
3 changes: 3 additions & 0 deletions src/server/routes/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub struct SendTxRequest {
pub priority: TransactionPriority,
#[serde(default)]
pub tx_id: Option<String>,
#[serde(default, with = "crate::serde_utils::base64_binary")]
pub blobs: Option<Vec<Vec<u8>>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -116,6 +118,7 @@ pub async fn send_tx(
req.value,
req.gas_limit,
req.priority,
req.blobs,
api_token.relayer_id(),
)
.await?;
Expand Down
3 changes: 3 additions & 0 deletions tests/send_too_many_txs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ async fn send_too_many_txs() -> eyre::Result<()> {
gas_limit: U256::from(21_000),
priority: TransactionPriority::Regular,
tx_id: None,
blobs: None,
},
)
.await?;
Expand All @@ -75,6 +76,7 @@ async fn send_too_many_txs() -> eyre::Result<()> {
gas_limit: U256::from(21_000),
priority: TransactionPriority::Regular,
tx_id: None,
blobs: None,
},
)
.await;
Expand Down Expand Up @@ -102,6 +104,7 @@ async fn send_too_many_txs() -> eyre::Result<()> {
gas_limit: U256::from(21_000),
priority: TransactionPriority::Regular,
tx_id: None,
blobs: None,
},
)
.await?;
Expand Down

0 comments on commit bfed77d

Please sign in to comment.