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

fix: Create trade after finished dlc protocol #2084

Merged
merged 4 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.lock

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

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ resolver = "2"
# We are using our own fork of `rust-dlc` at least until we can drop all the LN-DLC features. Also,
# `p2pderivatives/rust-dlc#master` is missing certain patches that can only be found in the LN-DLC
# branch.
dlc-manager = { git = "https://github.com/get10101/rust-dlc", rev = "0191dc4" }
dlc-messages = { git = "https://github.com/get10101/rust-dlc", rev = "0191dc4" }
dlc = { git = "https://github.com/get10101/rust-dlc", rev = "0191dc4" }
p2pd-oracle-client = { git = "https://github.com/get10101/rust-dlc", rev = "0191dc4" }
dlc-trie = { git = "https://github.com/get10101/rust-dlc", rev = "0191dc4" }
dlc-manager = { git = "https://github.com/get10101/rust-dlc", rev = "aa487662a8af75174bf0e60f24e07e171820d0c3" }
dlc-messages = { git = "https://github.com/get10101/rust-dlc", rev = "aa487662a8af75174bf0e60f24e07e171820d0c3" }
dlc = { git = "https://github.com/get10101/rust-dlc", rev = "aa487662a8af75174bf0e60f24e07e171820d0c3" }
p2pd-oracle-client = { git = "https://github.com/get10101/rust-dlc", rev = "aa487662a8af75174bf0e60f24e07e171820d0c3" }
dlc-trie = { git = "https://github.com/get10101/rust-dlc", rev = "aa487662a8af75174bf0e60f24e07e171820d0c3" }

# We should usually track the `p2pderivatives/split-tx-experiment[-10101]` branch. For now we depend
# on a special fork which removes a panic in `rust-lightning`.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
-- Your SQL goes here
ALTER TYPE "OrderState_Type" ADD VALUE 'Expired';
ALTER TYPE "OrderState_Type" ADD VALUE IF NOT EXISTS 'Expired';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DROP TABLE if exists trade_params;
DROP TABLE if exists dlc_protocols;

DROP TYPE IF EXISTS "Protocol_State_Type";
25 changes: 25 additions & 0 deletions coordinator/migrations/2024-02-27-113100_add_dlc_protocols/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

CREATE TYPE "Protocol_State_Type" AS ENUM ('Pending', 'Success', 'Failed');

CREATE TABLE "dlc_protocols"
(
id SERIAL PRIMARY KEY NOT NULL,
protocol_id UUID UNIQUE NOT NULL,
previous_protocol_id UUID REFERENCES dlc_protocols (protocol_id),
channel_id TEXT NOT NULL,
contract_id TEXT NOT NULL,
protocol_state "Protocol_State_Type" NOT NULL,
trader_pubkey TEXT NOT NULL REFERENCES users (pubkey),
timestamp timestamp WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE "trade_params"
(
id SERIAL PRIMARY KEY NOT NULL,
protocol_id UUID NOT NULL REFERENCES dlc_protocols(protocol_id),
trader_pubkey TEXT NOT NULL,
quantity REAL NOT NULL,
leverage REAL NOT NULL,
average_price REAL NOT NULL,
direction "Direction_Type" NOT NULL
);
1 change: 1 addition & 0 deletions coordinator/src/collaborative_revert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ pub fn confirm_collaborative_revert(
counter_party: signed_channel.counter_party,
temporary_channel_id: signed_channel.temporary_channel_id,
channel_id: signed_channel.channel_id,
reference_id: None,
}),
// The contract doesn't matter anymore.
None,
Expand Down
24 changes: 24 additions & 0 deletions coordinator/src/db/custom_types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::db::channels::ChannelState;
use crate::db::dlc_messages::MessageType;
use crate::db::dlc_protocols::DlcProtocolState;
use crate::db::payments::HtlcStatus;
use crate::db::payments::PaymentFlow;
use crate::db::polls::PollType;
Expand All @@ -13,6 +14,7 @@ use crate::schema::sql_types::MessageTypeType;
use crate::schema::sql_types::PaymentFlowType;
use crate::schema::sql_types::PollTypeType;
use crate::schema::sql_types::PositionStateType;
use crate::schema::sql_types::ProtocolStateType;
use diesel::deserialize;
use diesel::deserialize::FromSql;
use diesel::pg::Pg;
Expand Down Expand Up @@ -225,3 +227,25 @@ impl FromSql<PollTypeType, Pg> for PollType {
}
}
}

impl ToSql<ProtocolStateType, Pg> for DlcProtocolState {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
DlcProtocolState::Pending => out.write_all(b"Pending")?,
DlcProtocolState::Success => out.write_all(b"Success")?,
DlcProtocolState::Failed => out.write_all(b"Failed")?,
}
Ok(IsNull::No)
}
}

impl FromSql<ProtocolStateType, Pg> for DlcProtocolState {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"Pending" => Ok(DlcProtocolState::Pending),
b"Success" => Ok(DlcProtocolState::Success),
b"Failed" => Ok(DlcProtocolState::Failed),
_ => Err("Unrecognized enum variant for ContractTransactionType".into()),
}
}
}
162 changes: 162 additions & 0 deletions coordinator/src/db/dlc_protocols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use crate::dlc_protocol;
use crate::dlc_protocol::ProtocolId;
use crate::schema::dlc_protocols;
use crate::schema::sql_types::ProtocolStateType;
use bitcoin::hashes::hex::FromHex;
use bitcoin::hashes::hex::ToHex;
use bitcoin::secp256k1::PublicKey;
use diesel::query_builder::QueryId;
use diesel::AsExpression;
use diesel::ExpressionMethods;
use diesel::FromSqlRow;
use diesel::PgConnection;
use diesel::QueryDsl;
use diesel::QueryResult;
use diesel::Queryable;
use diesel::RunQueryDsl;
use dlc_manager::ContractId;
use dlc_manager::DlcChannelId;
use std::any::TypeId;
use std::str::FromStr;
use time::OffsetDateTime;
use uuid::Uuid;

#[derive(Debug, Clone, Copy, PartialEq, FromSqlRow, AsExpression, Eq, Hash)]
#[diesel(sql_type = ProtocolStateType)]
pub(crate) enum DlcProtocolState {
Pending,
Success,
Failed,
}

impl QueryId for ProtocolStateType {
type QueryId = ProtocolStateType;
const HAS_STATIC_QUERY_ID: bool = false;

fn query_id() -> Option<TypeId> {
None
}
}

#[derive(Queryable, Debug)]
#[diesel(table_name = protocols)]
#[allow(dead_code)] // We have to allow dead code here because diesel needs the fields to be able to derive queryable.
pub(crate) struct DlcProtocol {
pub id: i32,
pub protocol_id: Uuid,
pub previous_protocol_id: Option<Uuid>,
pub channel_id: String,
pub contract_id: String,
pub protocol_state: DlcProtocolState,
pub trader_pubkey: String,
pub timestamp: OffsetDateTime,
}

pub(crate) fn get_dlc_protocol(
conn: &mut PgConnection,
protocol_id: ProtocolId,
) -> QueryResult<dlc_protocol::DlcProtocol> {
let contract_transaction: DlcProtocol = dlc_protocols::table
.filter(dlc_protocols::protocol_id.eq(protocol_id.to_uuid()))
.first(conn)?;

Ok(dlc_protocol::DlcProtocol::from(contract_transaction))
}

pub(crate) fn set_dlc_protocol_state_to_failed(
conn: &mut PgConnection,
protocol_id: ProtocolId,
) -> QueryResult<()> {
let affected_rows = diesel::update(dlc_protocols::table)
.filter(dlc_protocols::protocol_id.eq(protocol_id.to_uuid()))
.set((dlc_protocols::protocol_state.eq(DlcProtocolState::Failed),))
.execute(conn)?;

if affected_rows == 0 {
return Err(diesel::result::Error::NotFound);
}

Ok(())
}

pub(crate) fn set_dlc_protocol_state_to_success(
conn: &mut PgConnection,
protocol_id: ProtocolId,
contract_id: ContractId,
channel_id: DlcChannelId,
) -> QueryResult<()> {
let affected_rows = diesel::update(dlc_protocols::table)
.filter(dlc_protocols::protocol_id.eq(protocol_id.to_uuid()))
.set((
dlc_protocols::protocol_state.eq(DlcProtocolState::Success),
dlc_protocols::contract_id.eq(contract_id.to_hex()),
dlc_protocols::channel_id.eq(channel_id.to_hex()),
))
.execute(conn)?;

if affected_rows == 0 {
return Err(diesel::result::Error::NotFound);
}

Ok(())
}

pub(crate) fn create(
conn: &mut PgConnection,
protocol_id: ProtocolId,
previous_protocol_id: Option<ProtocolId>,
contract_id: ContractId,
channel_id: DlcChannelId,
trader: &PublicKey,
) -> QueryResult<()> {
let affected_rows = diesel::insert_into(dlc_protocols::table)
.values(&(
dlc_protocols::protocol_id.eq(protocol_id.to_uuid()),
dlc_protocols::previous_protocol_id.eq(previous_protocol_id.map(|ppid| ppid.to_uuid())),
dlc_protocols::contract_id.eq(contract_id.to_hex()),
dlc_protocols::channel_id.eq(channel_id.to_hex()),
dlc_protocols::protocol_state.eq(DlcProtocolState::Pending),
dlc_protocols::trader_pubkey.eq(trader.to_string()),
dlc_protocols::timestamp.eq(OffsetDateTime::now_utc()),
))
.execute(conn)?;

if affected_rows == 0 {
return Err(diesel::result::Error::NotFound);
}

Ok(())
}

impl From<DlcProtocol> for dlc_protocol::DlcProtocol {
fn from(value: DlcProtocol) -> Self {
dlc_protocol::DlcProtocol {
id: value.protocol_id.into(),
timestamp: value.timestamp,
channel_id: DlcChannelId::from_hex(&value.channel_id).expect("valid dlc channel id"),
contract_id: ContractId::from_hex(&value.contract_id).expect("valid contract id"),
trader: PublicKey::from_str(&value.trader_pubkey).expect("valid public key"),
protocol_state: value.protocol_state.into(),
}
}
}

impl From<dlc_protocol::DlcProtocolState> for DlcProtocolState {
fn from(value: dlc_protocol::DlcProtocolState) -> Self {
match value {
dlc_protocol::DlcProtocolState::Pending => DlcProtocolState::Pending,
dlc_protocol::DlcProtocolState::Success => DlcProtocolState::Success,
dlc_protocol::DlcProtocolState::Failed => DlcProtocolState::Failed,
}
}
}

impl From<DlcProtocolState> for dlc_protocol::DlcProtocolState {
fn from(value: DlcProtocolState) -> Self {
match value {
DlcProtocolState::Pending => dlc_protocol::DlcProtocolState::Pending,
DlcProtocolState::Success => dlc_protocol::DlcProtocolState::Success,
DlcProtocolState::Failed => dlc_protocol::DlcProtocolState::Failed,
}
}
}
2 changes: 2 additions & 0 deletions coordinator/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod channels;
pub mod collaborative_reverts;
pub mod custom_types;
pub mod dlc_messages;
pub mod dlc_protocols;
pub mod last_outbound_dlc_message;
pub mod legacy_collaborative_reverts;
pub mod liquidity;
Expand All @@ -12,6 +13,7 @@ pub mod positions;
pub mod positions_helper;
pub mod routing_fees;
pub mod spendable_outputs;
pub mod trade_params;
pub mod trades;
pub mod transactions;
pub mod user;
24 changes: 8 additions & 16 deletions coordinator/src/db/positions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ impl Position {
conn: &mut PgConnection,
trader_pubkey: String,
state: crate::position::models::PositionState,
) -> Result<()> {
) -> QueryResult<crate::position::models::Position> {
let state = PositionState::from(state);
let affected_rows = diesel::update(positions::table)
let position: Position = diesel::update(positions::table)
.filter(positions::trader_pubkey.eq(trader_pubkey.clone()))
.filter(
positions::position_state
Expand All @@ -150,13 +150,9 @@ impl Position {
positions::position_state.eq(state),
positions::update_timestamp.eq(OffsetDateTime::now_utc()),
))
.execute(conn)?;

if affected_rows == 0 {
bail!("Could not update position to {state:?} for {trader_pubkey}")
}
.get_result(conn)?;

Ok(())
Ok(crate::position::models::Position::from(position))
}

/// sets the status of the position in state `Closing` to a new state
Expand Down Expand Up @@ -272,21 +268,17 @@ impl Position {
conn: &mut PgConnection,
id: i32,
pnl: i64,
) -> Result<()> {
let affected_rows = diesel::update(positions::table)
) -> QueryResult<crate::position::models::Position> {
let position: Position = diesel::update(positions::table)
.filter(positions::id.eq(id))
.set((
positions::position_state.eq(PositionState::Closed),
positions::trader_realized_pnl_sat.eq(Some(pnl)),
positions::update_timestamp.eq(OffsetDateTime::now_utc()),
))
.execute(conn)?;

if affected_rows == 0 {
bail!("Could not update position to Closed with realized pnl {pnl} for position {id}")
}
.get_result(conn)?;

Ok(())
Ok(crate::position::models::Position::from(position))
}

pub fn set_position_to_closed(conn: &mut PgConnection, id: i32) -> Result<()> {
Expand Down