Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wrapper structs for database related structs #201

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ bitcoin-mock-rpc = { workspace = true }
default = []
poc = []
mock_rpc = []
test-db = []

[[bin]]
name = "verifier"
Expand Down
44 changes: 15 additions & 29 deletions core/src/database/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use sqlx::{Pool, Postgres};
use std::fs;
use std::str::FromStr;

use super::wrapper::{AddressDB, EVMAddressDB, OutPointDB, SignatureDB, TxidDB};

#[derive(Clone, Debug)]
pub struct Database {
connection: Pool<Postgres>,
Expand Down Expand Up @@ -133,36 +135,22 @@ impl Database {
recovery_taproot_address: Address<NetworkUnchecked>,
evm_address: EVMAddress,
) -> Result<(), BridgeError> {
let start_utxo = start_utxo.to_string();
let recovery_taproot_address = serde_json::to_string(&recovery_taproot_address)
.unwrap()
.trim_matches('"')
.to_owned();
let evm_address = serde_json::to_string(&evm_address)
.unwrap()
.trim_matches('"')
.to_owned();

sqlx::query("INSERT INTO new_deposit_requests (start_utxo, recovery_taproot_address, evm_address) VALUES ($1, $2, $3);")
.bind(start_utxo)
.bind(recovery_taproot_address)
.bind(evm_address)
.fetch_all(&self.connection)
.bind(OutPointDB(start_utxo))
.bind(AddressDB(recovery_taproot_address))
.bind(EVMAddressDB(evm_address))
.execute(&self.connection)
.await?;

Ok(())
}

pub async fn get_deposit_tx(&self, idx: usize) -> Result<Txid, BridgeError> {
let qr: (String,) = sqlx::query_as("SELECT move_txid FROM deposit_move_txs WHERE id = $1;")
let qr: (TxidDB,) = sqlx::query_as("SELECT move_txid FROM deposit_move_txs WHERE id = $1;")
.bind(idx as i64)
.fetch_one(&self.connection)
.await?;

match Txid::from_str(qr.0.as_str()) {
Ok(c) => Ok(c),
Err(e) => Err(BridgeError::DatabaseError(sqlx::Error::Decode(Box::new(e)))),
}
Ok(qr.0 .0)
}

pub async fn get_next_deposit_index(&self) -> Result<usize, BridgeError> {
Expand Down Expand Up @@ -197,15 +185,13 @@ impl Database {
recovery_taproot_address: Address<NetworkUnchecked>,
evm_address: EVMAddress,
) -> Result<Txid, BridgeError> {
let qr: (String,) = sqlx::query_as("SELECT (move_txid) FROM deposit_move_txs WHERE start_utxo = $1 AND recovery_taproot_address = $2 AND evm_address = $3;")
.bind(start_utxo.to_string())
.bind(serde_json::to_string(&recovery_taproot_address).unwrap().trim_matches('"'))
.bind(serde_json::to_string(&evm_address).unwrap().trim_matches('"'))
let qr: (TxidDB,) = sqlx::query_as("SELECT (move_txid) FROM deposit_move_txs WHERE start_utxo = $1 AND recovery_taproot_address = $2 AND evm_address = $3;")
.bind(OutPointDB(start_utxo))
.bind(AddressDB(recovery_taproot_address))
.bind(EVMAddressDB(evm_address))
.fetch_one(&self.connection)
.await?;

let move_txid = Txid::from_str(&qr.0).unwrap();
Ok(move_txid)
Ok(qr.0 .0)
}

pub async fn save_withdrawal_sig(
Expand All @@ -218,8 +204,8 @@ impl Database {
"INSERT INTO withdrawal_sigs (idx, bridge_fund_txid, sig) VALUES ($1, $2, $3);",
)
.bind(idx as i64)
.bind(bridge_fund_txid.to_string())
.bind(sig.to_string())
.bind(TxidDB(bridge_fund_txid))
.bind(SignatureDB(sig))
.fetch_all(&self.connection)
.await?;

Expand Down
1 change: 1 addition & 0 deletions core/src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
pub mod common;
pub mod operator;
pub mod verifier;
pub mod wrapper;
255 changes: 255 additions & 0 deletions core/src/database/wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
use std::str::FromStr;

use bitcoin::{address::NetworkUnchecked, Address, OutPoint, Txid};
use serde::{Deserialize, Serialize};
use sqlx::{
postgres::{PgArgumentBuffer, PgRow, PgValueRef},
Decode, Encode, FromRow, Postgres, Row,
};

use crate::EVMAddress;

#[derive(Serialize, Deserialize)]
pub struct OutPointDB(pub OutPoint);

#[derive(Serialize)]
pub struct AddressDB(pub Address<NetworkUnchecked>);

#[derive(Serialize, Deserialize)]
pub struct EVMAddressDB(pub EVMAddress);

#[derive(Serialize, Deserialize)]
pub struct TxidDB(pub Txid);

#[derive(Serialize, Deserialize)]
pub struct SignatureDB(pub secp256k1::schnorr::Signature);

// Implement sqlx::Type manually if needed
impl sqlx::Type<sqlx::Postgres> for OutPointDB {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("TEXT")
}
}

impl<'q> Encode<'q, Postgres> for OutPointDB {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
// Encode as &str
let s = self.0.to_string();
<&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
}

fn encode(
self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull
where
Self: Sized,
{
self.encode_by_ref(buf)
}

fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
// `produces` is inherently a hook to allow database drivers to produce value-dependent
// type information; if the driver doesn't need this, it can leave this as `None`
None
}

fn size_hint(&self) -> usize {
std::mem::size_of_val(self)
}
}

impl<'r> Decode<'r, Postgres> for OutPointDB {
fn decode(value: PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as Decode<Postgres>>::decode(value)?;
Ok(OutPointDB(OutPoint::from_str(s)?)) // Assuming ExternalOutPoint has a from_string method
}
}

impl sqlx::Type<sqlx::Postgres> for AddressDB {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("TEXT")
}
}

impl<'q> Encode<'q, Postgres> for AddressDB {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
let s = self.0.clone().assume_checked().to_string();
<&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
}

fn encode(
self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull
where
Self: Sized,
{
self.encode_by_ref(buf)
}

fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
None
}

fn size_hint(&self) -> usize {
std::mem::size_of_val(self)
}
}

impl<'r> Decode<'r, Postgres> for AddressDB {
fn decode(value: PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as Decode<Postgres>>::decode(value)?;
Ok(AddressDB(Address::from_str(s)?)) // Assuming ExternalOutPoint has a from_string method
}
}

impl sqlx::Type<sqlx::Postgres> for EVMAddressDB {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("TEXT")
}
}

impl<'q> Encode<'q, Postgres> for EVMAddressDB {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
let s = hex::encode(self.0 .0);
<&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
}

fn encode(
self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull
where
Self: Sized,
{
self.encode_by_ref(buf)
}

fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
None
}

fn size_hint(&self) -> usize {
std::mem::size_of_val(self)
}
}

impl<'r> Decode<'r, Postgres> for EVMAddressDB {
fn decode(value: PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as Decode<Postgres>>::decode(value)?;
Ok(EVMAddressDB(EVMAddress(
hex::decode(s).unwrap().try_into().unwrap(),
)))
}
}

impl sqlx::Type<sqlx::Postgres> for TxidDB {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("TEXT")
}
}

impl<'q> Encode<'q, Postgres> for TxidDB {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
let s = hex::encode(self.0);
<&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
}

fn encode(
self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull
where
Self: Sized,
{
self.encode_by_ref(buf)
}

fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
None
}

fn size_hint(&self) -> usize {
std::mem::size_of_val(self)
}
}

impl<'r> Decode<'r, Postgres> for TxidDB {
fn decode(value: PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as Decode<Postgres>>::decode(value)?;
Ok(TxidDB(Txid::from_str(s).unwrap()))
}
}

impl<'r> FromRow<'r, PgRow> for TxidDB {
fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
let s = row.try_get_raw("move_txid").unwrap();
let str: &str = Decode::decode(s).map_err(|_| sqlx::Error::ColumnDecode {
index: "move_txid".into(),
source: Box::new(sqlx::Error::Decode("Invalid Txid".into())),
})?;
let res = Txid::from_str(str).map_err(|_| sqlx::Error::ColumnDecode {
index: "move_txid".into(),
source: Box::new(sqlx::Error::Decode("Invalid Txid".into())),
})?;
Ok(TxidDB(res))
}
}

impl sqlx::Type<sqlx::Postgres> for SignatureDB {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("TEXT")
}
}

impl<'q> Encode<'q, Postgres> for SignatureDB {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> sqlx::encode::IsNull {
let s = hex::encode(self.0.as_ref());
<&str as Encode<Postgres>>::encode_by_ref(&s.as_str(), buf)
}

fn encode(
self,
buf: &mut <Postgres as sqlx::database::HasArguments<'q>>::ArgumentBuffer,
) -> sqlx::encode::IsNull
where
Self: Sized,
{
self.encode_by_ref(buf)
}

fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
None
}

fn size_hint(&self) -> usize {
std::mem::size_of_val(self)
}
}

impl<'r> Decode<'r, Postgres> for SignatureDB {
fn decode(value: PgValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
let s = <&str as Decode<Postgres>>::decode(value)?;
Ok(SignatureDB(
secp256k1::schnorr::Signature::from_str(s).unwrap(),
))
}
}

impl<'r> FromRow<'r, PgRow> for SignatureDB {
fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
let s = row.try_get_raw("move_txid").unwrap();
let str: &str = Decode::decode(s).map_err(|_| sqlx::Error::ColumnDecode {
index: "move_txid".into(),
source: Box::new(sqlx::Error::Decode("Invalid Txid".into())),
})?;
let res = secp256k1::schnorr::Signature::from_str(str).map_err(|_| {
sqlx::Error::ColumnDecode {
index: "move_txid".into(),
source: Box::new(sqlx::Error::Decode("Invalid Txid".into())),
}
})?;
Ok(SignatureDB(res))
}
}
2 changes: 1 addition & 1 deletion core/tests/data/test_config_taproot.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ all_secret_keys = [
]
db_host = "127.0.0.1"
db_port = 5432
db_user = "citrea"
db_user = "clementine"
db_password = ""
db_name = "clementine"