Require chain ID field in payment options of ethereum type

This commit is contained in:
silverpill 2022-08-29 19:17:16 +00:00
parent 690a03946e
commit c5c3911de6
7 changed files with 65 additions and 17 deletions

View file

@ -0,0 +1,9 @@
UPDATE actor_profile
SET payment_options = (
-- remove all payment options except links
SELECT COALESCE (
jsonb_agg(opt) FILTER (WHERE opt ->> 'payment_type' = '1'),
'[]'
)
FROM jsonb_array_elements(actor_profile.payment_options) AS opt
);

View file

@ -68,8 +68,9 @@ pub fn attach_payment_option(
payment_option: PaymentOption,
) -> ActorAttachment {
match payment_option {
// Local actors can't have payment links
PaymentOption::Link(_) => unimplemented!(),
PaymentOption::EthereumSubscription => {
PaymentOption::EthereumSubscription(_) => {
let name = "EthereumSubscription".to_string();
let subscription_page_url =
get_subscription_page_url(instance_url, user_id);
@ -132,6 +133,7 @@ pub fn parse_extra_field(
#[cfg(test)]
mod tests {
use crate::utils::caip2::ChainId;
use crate::utils::id::new_uuid;
use super::*;
@ -155,7 +157,8 @@ mod tests {
#[test]
fn test_payment_option() {
let user_id = new_uuid();
let payment_option = PaymentOption::EthereumSubscription;
let payment_option =
PaymentOption::ethereum_subscription(ChainId::ethereum_mainnet());
let subscription_page_url =
format!("https://example.com/profile/{}/subscription", user_id);
let attachment = attach_payment_option(

View file

@ -12,9 +12,9 @@ pub async fn apply_migrations(db_client: &mut Client) {
for migration in migration_report.applied_migrations() {
log::info!(
"Migration Applied - Name: {}, Version: {}",
migration.name(),
"migration applied: version {} ({})",
migration.version(),
migration.name(),
);
}
}

View file

@ -98,7 +98,7 @@ impl Account {
.map(|option| {
match option {
PaymentOption::Link(link) => link.href,
PaymentOption::EthereumSubscription => {
PaymentOption::EthereumSubscription(_) => {
get_subscription_page_url(instance_url, &profile.id)
},
}

View file

@ -63,6 +63,9 @@ pub async fn subscriptions_enabled(
let mut maybe_payment_option = None;
match subscription_settings.into_inner() {
SubscriptionSettings::Ethereum => {
let ethereum_config = config.blockchain.as_ref()
.and_then(|conf| conf.ethereum_config())
.ok_or(HttpError::NotSupported)?;
let contract_set = maybe_blockchain.as_ref().as_ref()
.ok_or(HttpError::NotSupported)?;
let wallet_address = current_user
@ -78,7 +81,9 @@ pub async fn subscriptions_enabled(
if !is_registered {
return Err(ValidationError("recipient is not registered").into());
};
maybe_payment_option = Some(PaymentOption::EthereumSubscription);
maybe_payment_option = Some(PaymentOption::ethereum_subscription(
ethereum_config.chain_id.clone(),
));
};
},
SubscriptionSettings::Monero { } => {

View file

@ -15,6 +15,7 @@ use crate::activitypub::identifiers::local_actor_id;
use crate::database::json_macro::{json_from_sql, json_to_sql};
use crate::errors::{ConversionError, ValidationError};
use crate::ethereum::identity::DidPkh;
use crate::utils::caip2::ChainId;
use super::validators::{
validate_username,
validate_display_name,
@ -76,17 +77,26 @@ pub struct PaymentLink {
pub href: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EthereumSubscription {
chain_id: ChainId,
}
#[derive(Clone, Debug)]
pub enum PaymentOption {
Link(PaymentLink),
EthereumSubscription,
EthereumSubscription(EthereumSubscription),
}
impl PaymentOption {
pub fn ethereum_subscription(chain_id: ChainId) -> Self {
Self::EthereumSubscription(EthereumSubscription { chain_id })
}
fn payment_type(&self) -> PaymentType {
match self {
Self::Link(_) => PaymentType::Link,
Self::EthereumSubscription => PaymentType::EthereumSubscription,
Self::EthereumSubscription(_) => PaymentType::EthereumSubscription,
}
}
}
@ -109,7 +119,11 @@ impl<'de> Deserialize<'de> for PaymentOption {
.map_err(DeserializerError::custom)?;
Self::Link(link)
},
PaymentType::EthereumSubscription => Self::EthereumSubscription,
PaymentType::EthereumSubscription => {
let payment_info = EthereumSubscription::deserialize(value)
.map_err(DeserializerError::custom)?;
Self::EthereumSubscription(payment_info)
},
};
Ok(payment_option)
}
@ -125,7 +139,9 @@ impl Serialize for PaymentOption {
match self {
Self::Link(link) => link.serialize(FlatMapSerializer(&mut map))?,
Self::EthereumSubscription => (),
Self::EthereumSubscription(payment_info) => {
payment_info.serialize(FlatMapSerializer(&mut map))?
},
};
map.end()
}
@ -384,14 +400,15 @@ mod tests {
#[test]
fn test_payment_option_ethereum_subscription_serialization() {
let json_data = r#"{"payment_type":2,"name":null,"href":null}"#;
let json_data = r#"{"payment_type":2,"chain_id":"eip155:1","name":null}"#;
let payment_option: PaymentOption = serde_json::from_str(json_data).unwrap();
assert!(matches!(
payment_option,
PaymentOption::EthereumSubscription,
));
let payment_info = match payment_option {
PaymentOption::EthereumSubscription(ref payment_info) => payment_info,
_ => panic!("wrong option"),
};
assert_eq!(payment_info.chain_id, ChainId::ethereum_mainnet());
let serialized = serde_json::to_string(&payment_option).unwrap();
assert_eq!(serialized, r#"{"payment_type":2}"#);
assert_eq!(serialized, r#"{"payment_type":2,"chain_id":"eip155:1"}"#);
}
#[test]

View file

@ -2,7 +2,13 @@
use std::str::FromStr;
use regex::Regex;
use serde::{Deserialize, Deserializer, de::Error as DeserializerError};
use serde::{
Deserialize,
Deserializer,
Serialize,
Serializer,
de::Error as DeserializerError,
};
const CAIP2_RE: &str = r"(?P<namespace>[-a-z0-9]{3,8}):(?P<reference>[-a-zA-Z0-9]{1,32})";
const CAIP2_ETHEREUM_NAMESPACE: &str = "eip155";
@ -47,6 +53,14 @@ impl ToString for ChainId {
}
}
impl Serialize for ChainId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for ChainId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>