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

enforce swap size #29

Open
wants to merge 3 commits into
base: jssdwt-taproot-swaps
Choose a base branch
from
Open
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
30 changes: 18 additions & 12 deletions itest/tests/swap_pb2.py

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

47 changes: 45 additions & 2 deletions itest/tests/swap_pb2.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional
from typing import (
ClassVar as _ClassVar,
Mapping as _Mapping,
Optional as _Optional,
Union as _Union,
)

DESCRIPTOR: _descriptor.FileDescriptor

Expand All @@ -15,18 +20,21 @@ class CreateSwapRequest(_message.Message):
) -> None: ...

class CreateSwapResponse(_message.Message):
__slots__ = ("address", "claim_pubkey", "lock_height")
__slots__ = ("address", "claim_pubkey", "lock_height", "parameters")
ADDRESS_FIELD_NUMBER: _ClassVar[int]
CLAIM_PUBKEY_FIELD_NUMBER: _ClassVar[int]
LOCK_HEIGHT_FIELD_NUMBER: _ClassVar[int]
PARAMETERS_FIELD_NUMBER: _ClassVar[int]
address: str
claim_pubkey: bytes
lock_height: int
parameters: SwapParameters
def __init__(
self,
address: _Optional[str] = ...,
claim_pubkey: _Optional[bytes] = ...,
lock_height: _Optional[int] = ...,
parameters: _Optional[_Union[SwapParameters, _Mapping]] = ...,
) -> None: ...

class PaySwapRequest(_message.Message):
Expand Down Expand Up @@ -68,3 +76,38 @@ class RefundSwapResponse(_message.Message):
pub_nonce: _Optional[bytes] = ...,
partial_signature: _Optional[bytes] = ...,
) -> None: ...

class SwapParameters(_message.Message):
__slots__ = (
"lock_time",
"max_swap_amount_sat",
"min_swap_amount_sat",
"min_utxo_amount_sat",
)
LOCK_TIME_FIELD_NUMBER: _ClassVar[int]
MAX_SWAP_AMOUNT_SAT_FIELD_NUMBER: _ClassVar[int]
MIN_SWAP_AMOUNT_SAT_FIELD_NUMBER: _ClassVar[int]
MIN_UTXO_AMOUNT_SAT_FIELD_NUMBER: _ClassVar[int]
lock_time: int
max_swap_amount_sat: int
min_swap_amount_sat: int
min_utxo_amount_sat: int
def __init__(
self,
lock_time: _Optional[int] = ...,
max_swap_amount_sat: _Optional[int] = ...,
min_swap_amount_sat: _Optional[int] = ...,
min_utxo_amount_sat: _Optional[int] = ...,
) -> None: ...

class SwapParametersRequest(_message.Message):
__slots__ = ()
def __init__(self) -> None: ...

class SwapParametersResponse(_message.Message):
__slots__ = ("parameters",)
PARAMETERS_FIELD_NUMBER: _ClassVar[int]
parameters: SwapParameters
def __init__(
self, parameters: _Optional[_Union[SwapParameters, _Mapping]] = ...
) -> None: ...
47 changes: 47 additions & 0 deletions itest/tests/swap_pb2_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def __init__(self, channel):
response_deserializer=swap__pb2.RefundSwapResponse.FromString,
_registered_method=True,
)
self.SwapParameters = channel.unary_unary(
"/swap.Swapper/SwapParameters",
request_serializer=swap__pb2.SwapParametersRequest.SerializeToString,
response_deserializer=swap__pb2.SwapParametersResponse.FromString,
_registered_method=True,
)


class SwapperServicer(object):
Expand All @@ -78,6 +84,12 @@ def RefundSwap(self, request, context):
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")

def SwapParameters(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")


def add_SwapperServicer_to_server(servicer, server):
rpc_method_handlers = {
Expand All @@ -96,6 +108,11 @@ def add_SwapperServicer_to_server(servicer, server):
request_deserializer=swap__pb2.RefundSwapRequest.FromString,
response_serializer=swap__pb2.RefundSwapResponse.SerializeToString,
),
"SwapParameters": grpc.unary_unary_rpc_method_handler(
servicer.SwapParameters,
request_deserializer=swap__pb2.SwapParametersRequest.FromString,
response_serializer=swap__pb2.SwapParametersResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
"swap.Swapper", rpc_method_handlers
Expand Down Expand Up @@ -197,3 +214,33 @@ def RefundSwap(
metadata,
_registered_method=True,
)

@staticmethod
def SwapParameters(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/swap.Swapper/SwapParameters",
swap__pb2.SwapParametersRequest.SerializeToString,
swap__pb2.SwapParametersResponse.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True,
)
17 changes: 16 additions & 1 deletion swapd/proto/swap/swap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ service Swapper {
rpc CreateSwap (CreateSwapRequest) returns (CreateSwapResponse) {}
rpc PaySwap (PaySwapRequest) returns (PaySwapResponse) {}
rpc RefundSwap (RefundSwapRequest) returns (RefundSwapResponse) {}
rpc SwapParameters (SwapParametersRequest) returns (SwapParametersResponse) {}
}

message CreateSwapRequest {
Expand All @@ -16,6 +17,7 @@ message CreateSwapResponse {
string address = 1;
bytes claim_pubkey = 2;
uint32 lock_height = 3;
SwapParameters parameters = 4;
}

message PaySwapRequest {
Expand All @@ -34,4 +36,17 @@ message RefundSwapRequest {
message RefundSwapResponse {
bytes pub_nonce = 1;
bytes partial_signature = 2;
}
}

message SwapParameters {
uint32 lock_time = 1;
uint64 max_swap_amount_sat = 2;
uint64 min_swap_amount_sat = 3;
uint64 min_utxo_amount_sat = 4;
}

message SwapParametersRequest {}

message SwapParametersResponse {
SwapParameters parameters = 1;
}
63 changes: 55 additions & 8 deletions swapd/src/public_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ use crate::swap::{
};
use swap_api::{
swapper_server::Swapper, CreateSwapRequest, CreateSwapResponse, PaySwapRequest,
PaySwapResponse, RefundSwapRequest, RefundSwapResponse,
PaySwapResponse, RefundSwapRequest, RefundSwapResponse, SwapParameters, SwapParametersRequest,
SwapParametersResponse,
};

pub mod swap_api {
tonic::include_proto!("swap");
}

const FAKE_PREIMAGE: [u8; 32] = [0; 32];
const MIN_SWAP_AMOUNT_CONF_TARGET: i32 = 12;

pub struct SwapServerParams<C, CF, CR, L, P, R, F>
where
C: ChainClient,
Expand Down Expand Up @@ -123,6 +126,22 @@ where
fee_estimator: params.fee_estimator,
}
}

async fn get_swap_parameters(&self) -> Result<SwapParameters, Status> {
let fee_estimate = self
.fee_estimator
.estimate_fee(MIN_SWAP_AMOUNT_CONF_TARGET)
.await?;
// Assume a transaction weight of 1000.
let min_utxo_amount_sat = (fee_estimate.sat_per_kw as u64) * 3 / 2;

Ok(SwapParameters {
lock_time: self.swap_service.lock_time(),
max_swap_amount_sat: self.max_swap_amount_sat,
min_swap_amount_sat: min_utxo_amount_sat,
min_utxo_amount_sat,
})
}
}
#[tonic::async_trait]
impl<C, CF, CR, L, P, R, F> Swapper for SwapServer<C, CF, CR, L, P, R, F>
Expand Down Expand Up @@ -172,11 +191,12 @@ where
"new swap created"
);

// TODO: Add min/max allowed here?
let parameters = self.get_swap_parameters().await?;
Ok(Response::new(CreateSwapResponse {
address: swap.public.address.to_string(),
claim_pubkey: swap.public.claim_pubkey.serialize().to_vec(),
lock_height: swap.public.lock_height,
parameters: Some(parameters),
}))
}

Expand All @@ -185,7 +205,6 @@ where
&self,
request: Request<PaySwapRequest>,
) -> Result<Response<PaySwapResponse>, Status> {
// TODO: Ensure swap is not refunded and cannot be refunded at the same time.
debug!("pay_swap request");
let req = request.into_inner();
let invoice: Bolt11Invoice = req.payment_request.parse().map_err(|e| {
Expand All @@ -211,15 +230,23 @@ where
));
}

if amount_sat > self.max_swap_amount_sat {
let parameters = self.get_swap_parameters().await?;
if amount_sat > parameters.max_swap_amount_sat {
trace!(
amount_sat,
max_swap_amount_sat = self.max_swap_amount_sat,
max_swap_amount_sat = parameters.max_swap_amount_sat,
"invoice amount exceeds max swap amount"
);
return Err(Status::invalid_argument(
"amount exceeds maximum allowed deposit",
));
return Err(Status::invalid_argument("amount exceeds max swap amount"));
}

if amount_sat < parameters.min_swap_amount_sat {
trace!(
amount_sat,
min_swap_amount_sat = parameters.min_swap_amount_sat,
"invoice amount is below min swap amount"
);
return Err(Status::invalid_argument("amount is below min swap amount"));
}

let hash = invoice.payment_hash();
Expand Down Expand Up @@ -279,6 +306,16 @@ where
return false;
}

if utxo.tx_out.value.to_sat() < parameters.min_utxo_amount_sat {
debug!(
outpoint = field::display(utxo.outpoint),
utxo_amount_sat = utxo.tx_out.value.to_sat(),
min_utxo_amount_sat = parameters.min_utxo_amount_sat,
"utxo value is below min_utxo_amount_sat"
);
return false;
}

trace!(
outpoint = field::display(utxo.outpoint),
confirmations,
Expand Down Expand Up @@ -570,6 +607,16 @@ where
pub_nonce: our_pub_nonce.serialize().to_vec(),
}))
}

async fn swap_parameters(
&self,
_request: Request<SwapParametersRequest>,
) -> Result<Response<SwapParametersResponse>, Status> {
let parameters = self.get_swap_parameters().await?;
Ok(Response::new(SwapParametersResponse {
parameters: Some(parameters),
}))
}
}

impl From<SwapPersistenceError> for Status {
Expand Down
4 changes: 4 additions & 0 deletions swapd/src/swap/swap_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,10 @@ where
Ok(tx)
}

pub fn lock_time(&self) -> u32 {
self.lock_time
}

pub fn partial_sign_refund_tx(
&self,
swap: &Swap,
Expand Down
Loading