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

Support swap/swap claim in TransactionPlannerRequest #2947

Merged
merged 2 commits into from
Aug 18, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/bin/pcli/src/command/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ impl TxCmd {

let mut planner = Planner::new(OsRng);
planner.fee(swap_fee);
planner.swap(input, into, swap_claim_fee.clone(), claim_address)?;
planner.swap(input, into.id(), swap_claim_fee.clone(), claim_address)?;

let account_group_id = app.fvk.account_group_id();
let plan = planner
Expand Down
9 changes: 7 additions & 2 deletions crates/proto/src/gen/penumbra.view.v1alpha1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub mod transaction_planner_request {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Swap {
/// The amount and denomination to be traded in the Swap.
/// The input amount and denomination to be traded in the Swap.
#[prost(message, optional, tag = "1")]
pub value: ::core::option::Option<
super::super::super::core::crypto::v1alpha1::Value,
Expand All @@ -122,11 +122,16 @@ pub mod transaction_planner_request {
pub target_asset: ::core::option::Option<
super::super::super::core::crypto::v1alpha1::AssetId,
>,
/// An optional fee to be paid for performing the Swap.
/// The pre-paid fee to be paid for claiming the Swap outputs.
#[prost(message, optional, tag = "3")]
pub fee: ::core::option::Option<
super::super::super::core::crypto::v1alpha1::Fee,
>,
/// The address to which swap claim output will be sent.
#[prost(message, optional, tag = "4")]
pub claim_address: ::core::option::Option<
super::super::super::core::crypto::v1alpha1::Address,
>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down
18 changes: 18 additions & 0 deletions crates/proto/src/gen/penumbra.view.v1alpha1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5420,6 +5420,9 @@ impl serde::Serialize for transaction_planner_request::Swap {
if self.fee.is_some() {
len += 1;
}
if self.claim_address.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.view.v1alpha1.TransactionPlannerRequest.Swap", len)?;
if let Some(v) = self.value.as_ref() {
struct_ser.serialize_field("value", v)?;
Expand All @@ -5430,6 +5433,9 @@ impl serde::Serialize for transaction_planner_request::Swap {
if let Some(v) = self.fee.as_ref() {
struct_ser.serialize_field("fee", v)?;
}
if let Some(v) = self.claim_address.as_ref() {
struct_ser.serialize_field("claimAddress", v)?;
}
struct_ser.end()
}
}
Expand All @@ -5444,13 +5450,16 @@ impl<'de> serde::Deserialize<'de> for transaction_planner_request::Swap {
"target_asset",
"targetAsset",
"fee",
"claim_address",
"claimAddress",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Value,
TargetAsset,
Fee,
ClaimAddress,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
Expand All @@ -5475,6 +5484,7 @@ impl<'de> serde::Deserialize<'de> for transaction_planner_request::Swap {
"value" => Ok(GeneratedField::Value),
"targetAsset" | "target_asset" => Ok(GeneratedField::TargetAsset),
"fee" => Ok(GeneratedField::Fee),
"claimAddress" | "claim_address" => Ok(GeneratedField::ClaimAddress),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
Expand All @@ -5497,6 +5507,7 @@ impl<'de> serde::Deserialize<'de> for transaction_planner_request::Swap {
let mut value__ = None;
let mut target_asset__ = None;
let mut fee__ = None;
let mut claim_address__ = None;
while let Some(k) = map.next_key()? {
match k {
GeneratedField::Value => {
Expand All @@ -5517,12 +5528,19 @@ impl<'de> serde::Deserialize<'de> for transaction_planner_request::Swap {
}
fee__ = map.next_value()?;
}
GeneratedField::ClaimAddress => {
if claim_address__.is_some() {
return Err(serde::de::Error::duplicate_field("claimAddress"));
}
claim_address__ = map.next_value()?;
}
}
}
Ok(transaction_planner_request::Swap {
value: value__,
target_asset: target_asset__,
fee: fee__,
claim_address: claim_address__,
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/proto/src/gen/proto_descriptor.bin
Git LFS file not shown
38 changes: 21 additions & 17 deletions crates/view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,37 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = []
default = ["std"]
# When this feature is enabled, the view worker will request every single
# SCT root, to pinpoint exactly where any SCT root divergence occurs.
sct-divergence-check = []
std = ["ark-std/std"]

[dependencies]
# Workspace dependencies
penumbra-proto = { path = "../proto", features = ["rpc"] }
penumbra-tct = { path = "../crypto/tct" }
penumbra-num = { path = "../core/num" }
penumbra-keys = { path = "../core/keys" }
penumbra-asset = { path = "../core/asset" }
penumbra-chain = { path = "../core/component/chain" }
penumbra-shielded-pool = { path = "../core/component/shielded-pool", default-features = false, features = ["proving-keys"] }
penumbra-stake = { path = "../core/component/stake", default-features = false }
penumbra-ibc = { path = "../core/component/ibc", default-features = false }
penumbra-dao = { path = "../core/component/dao", default-features = false }
penumbra-dex = { path = "../core/component/dex", default-features = false }
penumbra-sct = { path = "../core/component/sct", default-features = false }
penumbra-fee = { path = "../core/component/fee", default-features = false }
penumbra-proto = { path = "../proto", features = ["rpc"] }
penumbra-tct = { path = "../crypto/tct" }
penumbra-num = { path = "../core/num" }
penumbra-keys = { path = "../core/keys" }
penumbra-asset = { path = "../core/asset" }
penumbra-chain = { path = "../core/component/chain" }
penumbra-shielded-pool = { path = "../core/component/shielded-pool", default-features = false, features = [
"proving-keys",
] }
penumbra-stake = { path = "../core/component/stake", default-features = false }
penumbra-ibc = { path = "../core/component/ibc", default-features = false }
penumbra-dao = { path = "../core/component/dao", default-features = false }
penumbra-dex = { path = "../core/component/dex", default-features = false }
penumbra-sct = { path = "../core/component/sct", default-features = false }
penumbra-fee = { path = "../core/component/fee", default-features = false }
penumbra-compact-block = { path = "../core/component/compact-block", default-features = false }
penumbra-app = { path = "../core/app" }
penumbra-transaction = { path = "../core/transaction" }
penumbra-app = { path = "../core/app" }
penumbra-transaction = { path = "../core/transaction" }

ibc-types = { git = "https://github.com/penumbra-zone/ibc-types", rev = "99d1484398f13aa35578948d34537bb8bb9848d9", default-features = false }

decaf377 = {version = "0.5", features = ["r1cs"] }
ark-std = { version = "0.4", default-features = false }
decaf377 = { version = "0.5", features = ["r1cs"] }
tokio = { version = "1.22", features = ["full"] }
tokio-stream = { version = "0.1.8", features = ["sync"] }
anyhow = "1"
Expand Down
6 changes: 3 additions & 3 deletions crates/view/src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{

use anyhow::Result;

use penumbra_asset::{asset::DenomMetadata, Balance, Value};
use penumbra_asset::{asset, Balance, Value};
use penumbra_chain::params::{ChainParameters, FmdParameters};
use penumbra_dao::DaoDeposit;
use penumbra_dex::{
Expand Down Expand Up @@ -210,13 +210,13 @@ impl<R: RngCore + CryptoRng> Planner<R> {
pub fn swap(
&mut self,
input_value: Value,
into_denom: DenomMetadata,
into_asset: asset::Id,
swap_claim_fee: Fee,
claim_address: Address,
) -> Result<&mut Self> {
// Determine the canonical order for the assets being swapped.
// This will determine whether the input amount is assigned to delta_1 or delta_2.
let trading_pair = TradingPair::new(input_value.asset_id, into_denom.id());
let trading_pair = TradingPair::new(input_value.asset_id, into_asset);

// If `trading_pair.asset_1` is the input asset, then `delta_1` is the input amount,
// and `delta_2` is 0.
Expand Down
86 changes: 79 additions & 7 deletions crates/view/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ use std::{
};

use anyhow::{anyhow, Context};
use ark_std::UniformRand;
use async_stream::try_stream;
use camino::Utf8Path;
use decaf377::Fq;
use futures::stream::{StreamExt, TryStreamExt};
use penumbra_asset::{asset, Value};
use penumbra_dex::{
lp::{
position::{self, Position},
Reserves,
},
swap_claim::SwapClaimPlan,
TradingPair,
};
use penumbra_fee::Fee;
Expand Down Expand Up @@ -355,6 +358,11 @@ impl ViewProtocolService for ViewService {
) -> Result<tonic::Response<pb::TransactionPlannerResponse>, tonic::Status> {
let prq = request.into_inner();

let chain_params =
self.storage.chain_params().await.map_err(|e| {
tonic::Status::internal(format!("could not get chain params: {:#}", e))
})?;

let mut planner = Planner::new(OsRng);
planner
.fee(
Expand Down Expand Up @@ -389,11 +397,75 @@ impl ViewProtocolService for ViewService {
planner.output(value, address);
}

#[allow(clippy::never_loop)]
for _swap in prq.swaps {
return Err(tonic::Status::unimplemented(
"Swaps are not yet implemented, sorry!",
));
for swap in prq.swaps {
let value: Value = swap
.value
.ok_or_else(|| tonic::Status::invalid_argument("Missing value"))?
.try_into()
.map_err(|e| {
tonic::Status::invalid_argument(format!("Could not parse value: {e:#}"))
})?;

let target_asset: asset::Id = swap
.target_asset
.ok_or_else(|| tonic::Status::invalid_argument("Missing target asset"))?
.try_into()
.map_err(|e| {
tonic::Status::invalid_argument(format!("Could not parse target asset: {e:#}"))
})?;

let fee: Fee = swap
.fee
.ok_or_else(|| tonic::Status::invalid_argument("Missing fee"))?
.try_into()
.map_err(|e| {
tonic::Status::invalid_argument(format!("Could not parse fee: {e:#}"))
})?;

let claim_address: Address = swap
.claim_address
.ok_or_else(|| tonic::Status::invalid_argument("Missing claim address"))?
.try_into()
.map_err(|e| {
tonic::Status::invalid_argument(format!("Could not parse claim address: {e:#}"))
})?;

planner
.swap(value, target_asset, fee, claim_address)
.map_err(|e| {
tonic::Status::invalid_argument(format!("Could not plan swap: {e:#}"))
})?;
}

for swap_claim in prq.swap_claims {
let swap_commitment: StateCommitment = swap_claim
.swap_commitment
.ok_or_else(|| tonic::Status::invalid_argument("Missing swap commitment"))?
.try_into()
.map_err(|e| {
tonic::Status::invalid_argument(format!(
"Could not parse swap commitment: {e:#}"
))
})?;
let swap_record = self
.storage
// TODO: should there be a timeout on detection here instead?
.swap_by_commitment(swap_commitment, false)
.await
.map_err(|e| {
tonic::Status::invalid_argument(format!(
"Could not fetch swap by commitment: {e:#}"
))
})?;

planner.swap_claim(SwapClaimPlan {
swap_plaintext: swap_record.swap,
position: swap_record.position,
output_data: swap_record.output_data,
epoch_duration: chain_params.epoch_duration,
proof_blinding_r: Fq::rand(&mut OsRng),
proof_blinding_s: Fq::rand(&mut OsRng),
});
}

for delegation in prq.delegations {
Expand Down Expand Up @@ -490,11 +562,11 @@ impl ViewProtocolService for ViewService {
planner.position_withdraw(position_id, reserves, trading_pair);
}

let mut client_of_self =
ViewProtocolServiceClient::new(ViewProtocolServiceServer::new(self.clone()));
let fvk = self.storage.full_viewing_key().await.map_err(|e| {
tonic::Status::failed_precondition(format!("Error retrieving full viewing key: {e:#}"))
})?;
let mut client_of_self =
ViewProtocolServiceClient::new(ViewProtocolServiceServer::new(self.clone()));
let plan = planner
.plan(&mut client_of_self, fvk.account_group_id(), 0u32.into())
.await
Expand Down
Loading
Loading