|
| 1 | +use std::str::FromStr; |
| 2 | + |
| 3 | +use juniper::{GraphQLInputObject, GraphQLObject}; |
| 4 | +use ledger::scan_state::{ |
| 5 | + currency::{Amount, Fee, Magnitude, Nonce, Slot}, |
| 6 | + transaction_logic::{signed_command, Memo}, |
| 7 | +}; |
| 8 | +use mina_p2p_messages::{ |
| 9 | + bigint::BigInt, |
| 10 | + v2::{self, TokenIdKeyHash}, |
| 11 | +}; |
| 12 | +use mina_signer::CompressedPubKey; |
| 13 | +use node::account::AccountPublicKey; |
| 14 | +use o1_utils::field_helpers::FieldHelpers; |
| 15 | + |
| 16 | +use super::zkapp::GraphQLFailureReason; |
| 17 | + |
| 18 | +// #[derive(GraphQLInputObject, Debug)] |
| 19 | +// pub struct InputGraphQLSendPayment { |
| 20 | +// pub input: InputGraphQLPayment, |
| 21 | +// pub signature: UserCommandSignature, |
| 22 | +// } |
| 23 | + |
| 24 | +#[derive(GraphQLInputObject, Debug)] |
| 25 | +pub struct InputGraphQLPayment { |
| 26 | + pub from: String, |
| 27 | + pub to: String, |
| 28 | + pub amount: String, |
| 29 | + pub valid_until: Option<String>, |
| 30 | + pub fee: String, |
| 31 | + pub memo: Option<String>, |
| 32 | + pub nonce: Option<String>, |
| 33 | +} |
| 34 | + |
| 35 | +#[derive(GraphQLInputObject, Debug, Clone)] |
| 36 | +pub struct UserCommandSignature { |
| 37 | + pub field: Option<String>, |
| 38 | + pub scalar: Option<String>, |
| 39 | + // Note: either raw_signature or scalar + field must be provided |
| 40 | + pub raw_signature: Option<String>, |
| 41 | +} |
| 42 | + |
| 43 | +impl TryFrom<UserCommandSignature> for mina_signer::Signature { |
| 44 | + type Error = super::ConversionError; |
| 45 | + |
| 46 | + fn try_from(value: UserCommandSignature) -> Result<Self, Self::Error> { |
| 47 | + let UserCommandSignature { |
| 48 | + field, |
| 49 | + scalar, |
| 50 | + raw_signature, |
| 51 | + } = value; |
| 52 | + |
| 53 | + if let Some(raw_signature) = raw_signature { |
| 54 | + let sig_parts_len = raw_signature |
| 55 | + .len() |
| 56 | + .checked_div(2) |
| 57 | + .ok_or(super::ConversionError::InvalidLength)?; |
| 58 | + let (rx_hex, s_hex) = raw_signature.split_at(sig_parts_len); |
| 59 | + |
| 60 | + let rx_bytes = hex::decode(rx_hex).map_err(|_| super::ConversionError::InvalidHex)?; |
| 61 | + let s_bytes = hex::decode(s_hex).map_err(|_| super::ConversionError::InvalidHex)?; |
| 62 | + |
| 63 | + let rx = mina_signer::BaseField::from_bytes(&rx_bytes)?; |
| 64 | + let s = mina_signer::ScalarField::from_bytes(&s_bytes)?; |
| 65 | + |
| 66 | + Ok(Self { rx, s }) |
| 67 | + } else if let (Some(field), Some(scalar)) = (field, scalar) { |
| 68 | + let sig = Self { |
| 69 | + rx: BigInt::from_decimal(&field)? |
| 70 | + .try_into() |
| 71 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 72 | + s: BigInt::from_decimal(&scalar)? |
| 73 | + .try_into() |
| 74 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 75 | + }; |
| 76 | + |
| 77 | + Ok(sig) |
| 78 | + } else { |
| 79 | + Err(super::ConversionError::MissingField( |
| 80 | + "Either raw_signature or scalar + field must be provided".to_string(), |
| 81 | + )) |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +impl TryFrom<&UserCommandSignature> for mina_signer::Signature { |
| 87 | + type Error = super::ConversionError; |
| 88 | + |
| 89 | + fn try_from(value: &UserCommandSignature) -> Result<Self, Self::Error> { |
| 90 | + value.clone().try_into() |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +impl UserCommandSignature { |
| 95 | + pub fn validate(&self) -> Result<(), super::Error> { |
| 96 | + if self.raw_signature.is_some() || (self.scalar.is_some() && self.field.is_some()) { |
| 97 | + Ok(()) |
| 98 | + } else { |
| 99 | + Err(super::Error::Custom( |
| 100 | + "Either raw_signature or scalar + field must be provided".to_string(), |
| 101 | + )) |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +#[derive(GraphQLObject, Debug)] |
| 107 | +pub struct GraphQLSendPaymentResponse { |
| 108 | + pub payment: GraphQLUserCommand, |
| 109 | +} |
| 110 | + |
| 111 | +#[derive(GraphQLObject, Debug)] |
| 112 | +pub struct GraphQLUserCommand { |
| 113 | + pub amount: String, |
| 114 | + pub fee: String, |
| 115 | + pub failure_reason: Option<GraphQLFailureReason>, |
| 116 | + // TODO: add the account type |
| 117 | + pub fee_payer: String, |
| 118 | + pub fee_token: String, |
| 119 | + pub hash: String, |
| 120 | + pub id: String, |
| 121 | + pub is_delegation: bool, |
| 122 | + pub kind: String, |
| 123 | + pub memo: String, |
| 124 | + pub nonce: String, |
| 125 | + // TODO: add the account type |
| 126 | + pub receiver: String, |
| 127 | + // TODO: add the account type |
| 128 | + pub source: String, |
| 129 | + pub token: String, |
| 130 | + pub valid_until: String, |
| 131 | +} |
| 132 | + |
| 133 | +impl TryFrom<v2::MinaBaseUserCommandStableV2> for GraphQLSendPaymentResponse { |
| 134 | + type Error = super::ConversionError; |
| 135 | + fn try_from(value: v2::MinaBaseUserCommandStableV2) -> Result<Self, Self::Error> { |
| 136 | + if let v2::MinaBaseUserCommandStableV2::SignedCommand(ref signed_cmd) = value { |
| 137 | + if let v2::MinaBaseSignedCommandPayloadBodyStableV2::Payment(ref payment) = |
| 138 | + signed_cmd.payload.body |
| 139 | + { |
| 140 | + let res = GraphQLSendPaymentResponse { |
| 141 | + payment: GraphQLUserCommand { |
| 142 | + amount: payment.amount.to_string(), |
| 143 | + fee: signed_cmd.payload.common.fee.to_string(), |
| 144 | + failure_reason: None, |
| 145 | + fee_payer: signed_cmd.payload.common.fee_payer_pk.to_string(), |
| 146 | + fee_token: TokenIdKeyHash::default().to_string(), |
| 147 | + hash: signed_cmd.hash()?.to_string(), |
| 148 | + id: signed_cmd.to_base64()?, |
| 149 | + is_delegation: false, |
| 150 | + kind: "Payment".to_string(), |
| 151 | + memo: signed_cmd.payload.common.memo.to_base58check(), |
| 152 | + nonce: signed_cmd.payload.common.nonce.to_string(), |
| 153 | + receiver: payment.receiver_pk.to_string(), |
| 154 | + source: signed_cmd.payload.common.fee_payer_pk.to_string(), |
| 155 | + token: TokenIdKeyHash::default().to_string(), |
| 156 | + valid_until: signed_cmd.payload.common.valid_until.as_u32().to_string(), |
| 157 | + }, |
| 158 | + }; |
| 159 | + Ok(res) |
| 160 | + } else { |
| 161 | + Err(super::ConversionError::WrongVariant) |
| 162 | + } |
| 163 | + } else { |
| 164 | + Err(super::ConversionError::WrongVariant) |
| 165 | + } |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +impl InputGraphQLPayment { |
| 170 | + pub fn create_user_command( |
| 171 | + &self, |
| 172 | + infered_nonce: Nonce, |
| 173 | + signature: UserCommandSignature, |
| 174 | + ) -> Result<v2::MinaBaseUserCommandStableV2, super::ConversionError> { |
| 175 | + let infered_nonce = infered_nonce.incr(); |
| 176 | + |
| 177 | + let nonce = if let Some(nonce) = &self.nonce { |
| 178 | + let input_nonce = Nonce::from_u32( |
| 179 | + nonce |
| 180 | + .parse::<u32>() |
| 181 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 182 | + ); |
| 183 | + |
| 184 | + if input_nonce.is_zero() || input_nonce > infered_nonce { |
| 185 | + return Err(super::ConversionError::Custom( |
| 186 | + "Provided nonce is zero or greater than infered nonce".to_string(), |
| 187 | + )); |
| 188 | + } else { |
| 189 | + input_nonce |
| 190 | + } |
| 191 | + } else { |
| 192 | + infered_nonce |
| 193 | + }; |
| 194 | + |
| 195 | + let valid_until = if let Some(valid_until) = &self.valid_until { |
| 196 | + Some(Slot::from_u32( |
| 197 | + valid_until |
| 198 | + .parse::<u32>() |
| 199 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 200 | + )) |
| 201 | + } else { |
| 202 | + None |
| 203 | + }; |
| 204 | + |
| 205 | + let memo = if let Some(memo) = &self.memo { |
| 206 | + Memo::from_str(memo) |
| 207 | + .map_err(|_| super::ConversionError::Custom("Invalid memo".to_string()))? |
| 208 | + } else { |
| 209 | + Memo::empty() |
| 210 | + }; |
| 211 | + |
| 212 | + let from: CompressedPubKey = AccountPublicKey::from_str(&self.from)? |
| 213 | + .try_into() |
| 214 | + .map_err(|_| super::ConversionError::InvalidBigInt)?; |
| 215 | + |
| 216 | + let signature = signature.try_into()?; |
| 217 | + |
| 218 | + let sc: signed_command::SignedCommand = signed_command::SignedCommand { |
| 219 | + payload: signed_command::SignedCommandPayload::create( |
| 220 | + Fee::from_u64( |
| 221 | + self.fee |
| 222 | + .parse::<u64>() |
| 223 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 224 | + ), |
| 225 | + from.clone(), |
| 226 | + nonce, |
| 227 | + valid_until, |
| 228 | + memo, |
| 229 | + signed_command::Body::Payment(signed_command::PaymentPayload { |
| 230 | + receiver_pk: AccountPublicKey::from_str(&self.to)? |
| 231 | + .try_into() |
| 232 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 233 | + amount: Amount::from_u64( |
| 234 | + self.amount |
| 235 | + .parse::<u64>() |
| 236 | + .map_err(|_| super::ConversionError::InvalidBigInt)?, |
| 237 | + ), |
| 238 | + }), |
| 239 | + ), |
| 240 | + signer: from.clone(), |
| 241 | + signature, |
| 242 | + }; |
| 243 | + |
| 244 | + Ok(v2::MinaBaseUserCommandStableV2::SignedCommand(sc.into())) |
| 245 | + } |
| 246 | +} |
| 247 | +// impl TryFrom<InputGraphQLSendPayment> for v2::MinaBaseUserCommandStableV2 { |
| 248 | +// type Error = super::ConversionError; |
| 249 | + |
| 250 | +// fn try_from(value: InputGraphQLSendPayment) -> Result<Self, Self::Error> { |
| 251 | +// let InputGraphQLSendPayment { input, signature } = value; |
| 252 | + |
| 253 | +// } |
| 254 | +// } |
0 commit comments