mirror of
https://github.com/creating2morrow/neveko.git
synced 2025-01-18 16:54:38 +00:00
enforce subaddress for tx proof and jwp
This commit is contained in:
parent
847aaeb0d0
commit
f821c4b572
5 changed files with 106 additions and 25 deletions
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: Release Artifacts
|
name: cargo-build-release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
|
@ -17,7 +17,8 @@ enum RpcFields {
|
||||||
Balance,
|
Balance,
|
||||||
CheckTxProof,
|
CheckTxProof,
|
||||||
Close,
|
Close,
|
||||||
Create,
|
CreateAddress,
|
||||||
|
CreateWallet,
|
||||||
Export,
|
Export,
|
||||||
Finalize,
|
Finalize,
|
||||||
GetTxProof,
|
GetTxProof,
|
||||||
|
@ -43,7 +44,8 @@ impl RpcFields {
|
||||||
RpcFields::Balance => String::from("get_balance"),
|
RpcFields::Balance => String::from("get_balance"),
|
||||||
RpcFields::CheckTxProof => String::from("check_tx_proof"),
|
RpcFields::CheckTxProof => String::from("check_tx_proof"),
|
||||||
RpcFields::Close => String::from("close_wallet"),
|
RpcFields::Close => String::from("close_wallet"),
|
||||||
RpcFields::Create => String::from("create_wallet"),
|
RpcFields::CreateAddress => String::from("create_address"),
|
||||||
|
RpcFields::CreateWallet => String::from("create_wallet"),
|
||||||
RpcFields::Export => String::from("export_multisig_info"),
|
RpcFields::Export => String::from("export_multisig_info"),
|
||||||
RpcFields::Finalize => String::from("finalize_multisig"),
|
RpcFields::Finalize => String::from("finalize_multisig"),
|
||||||
RpcFields::GetTxProof => String::from("get_tx_proof"),
|
RpcFields::GetTxProof => String::from("get_tx_proof"),
|
||||||
|
@ -278,7 +280,7 @@ pub async fn create_wallet(filename: String) -> bool {
|
||||||
let req = reqres::XmrRpcCreateRequest {
|
let req = reqres::XmrRpcCreateRequest {
|
||||||
jsonrpc: RpcFields::JsonRpcVersion.value(),
|
jsonrpc: RpcFields::JsonRpcVersion.value(),
|
||||||
id: RpcFields::Id.value(),
|
id: RpcFields::Id.value(),
|
||||||
method: RpcFields::Create.value(),
|
method: RpcFields::CreateWallet.value(),
|
||||||
params,
|
params,
|
||||||
};
|
};
|
||||||
let login: RpcLogin = get_rpc_creds();
|
let login: RpcLogin = get_rpc_creds();
|
||||||
|
@ -630,7 +632,7 @@ pub async fn check_tx_proof(txp: &proof::TxProof) -> reqres::XmrRpcCheckTxProofR
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let host = get_rpc_host();
|
let host = get_rpc_host();
|
||||||
let params: reqres::XmrRpcCheckTxProofParams = reqres::XmrRpcCheckTxProofParams {
|
let params: reqres::XmrRpcCheckTxProofParams = reqres::XmrRpcCheckTxProofParams {
|
||||||
address: String::from(&txp.address),
|
address: String::from(&txp.subaddress),
|
||||||
message: String::from(&txp.message),
|
message: String::from(&txp.message),
|
||||||
signature: String::from(&txp.signature),
|
signature: String::from(&txp.signature),
|
||||||
txid: String::from(&txp.hash),
|
txid: String::from(&txp.hash),
|
||||||
|
@ -662,7 +664,7 @@ pub async fn get_tx_proof(ptxp: proof::TxProof) -> reqres::XmrRpcGetTxProofRespo
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let host = get_rpc_host();
|
let host = get_rpc_host();
|
||||||
let params: reqres::XmrRpcGetTxProofParams = reqres::XmrRpcGetTxProofParams {
|
let params: reqres::XmrRpcGetTxProofParams = reqres::XmrRpcGetTxProofParams {
|
||||||
address: String::from(&ptxp.address),
|
address: String::from(&ptxp.subaddress),
|
||||||
message: String::from(&ptxp.message),
|
message: String::from(&ptxp.message),
|
||||||
txid: String::from(&ptxp.hash),
|
txid: String::from(&ptxp.hash),
|
||||||
};
|
};
|
||||||
|
@ -779,6 +781,33 @@ pub async fn sweep_all(address: String) -> reqres::XmrRpcSweepAllResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs the xmr rpc 'create_address' method
|
||||||
|
pub async fn create_address() -> reqres::XmrRpcCreateAddressResponse {
|
||||||
|
info!("creating new subaddress");
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let host = get_rpc_host();
|
||||||
|
let params: reqres::XmrRpcCreateAddressParams = reqres::XmrRpcCreateAddressParams { account_index: 0 };
|
||||||
|
let req = reqres::XmrRpcCreateAddressRequest {
|
||||||
|
jsonrpc: RpcFields::JsonRpcVersion.value(),
|
||||||
|
id: RpcFields::Id.value(),
|
||||||
|
method: RpcFields::CreateAddress.value(),
|
||||||
|
params,
|
||||||
|
};
|
||||||
|
let login: RpcLogin = get_rpc_creds();
|
||||||
|
match client.post(host).json(&req)
|
||||||
|
.send_with_digest_auth(&login.username, &login.credential).await {
|
||||||
|
Ok(response) => {
|
||||||
|
let res = response.json::<reqres::XmrRpcCreateAddressResponse>().await;
|
||||||
|
debug!("{} response: {:?}", RpcFields::CreateAddress.value(), res);
|
||||||
|
match res {
|
||||||
|
Ok(res) => res,
|
||||||
|
_ => Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Daemon requests
|
// Daemon requests
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
/// Performs the xmr daemon 'get_info' method
|
/// Performs the xmr daemon 'get_info' method
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct TxProof {
|
pub struct TxProof {
|
||||||
pub address: String,
|
pub subaddress: String,
|
||||||
pub confirmations: u64,
|
pub confirmations: u64,
|
||||||
pub hash: String,
|
pub hash: String,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
|
@ -24,7 +24,7 @@ pub struct TxProof {
|
||||||
impl Default for TxProof {
|
impl Default for TxProof {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TxProof {
|
TxProof {
|
||||||
address: utils::empty_string(),
|
subaddress: utils::empty_string(),
|
||||||
confirmations: 0,
|
confirmations: 0,
|
||||||
hash: utils::empty_string(),
|
hash: utils::empty_string(),
|
||||||
message: utils::empty_string(),
|
message: utils::empty_string(),
|
||||||
|
@ -38,8 +38,9 @@ impl Default for TxProof {
|
||||||
/// provide proof of payment.
|
/// provide proof of payment.
|
||||||
pub async fn create_invoice() -> reqres::Invoice {
|
pub async fn create_invoice() -> reqres::Invoice {
|
||||||
info!("creating invoice");
|
info!("creating invoice");
|
||||||
let m_address = monero::get_address().await;
|
// create a new subaddress
|
||||||
let address = m_address.result.address;
|
let c_address = monero::create_address().await;
|
||||||
|
let address = c_address.result.address;
|
||||||
let pay_threshold = utils::get_payment_threshold();
|
let pay_threshold = utils::get_payment_threshold();
|
||||||
let conf_threshold = utils::get_conf_threshold();
|
let conf_threshold = utils::get_conf_threshold();
|
||||||
reqres::Invoice { address, conf_threshold, pay_threshold }
|
reqres::Invoice { address, conf_threshold, pay_threshold }
|
||||||
|
@ -70,14 +71,14 @@ pub async fn create_jwp(proof: &TxProof) -> String {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let mut claims = BTreeMap::new();
|
let mut claims = BTreeMap::new();
|
||||||
let address = &proof.address;
|
let address = &proof.subaddress;
|
||||||
let created = chrono::Utc::now().timestamp();
|
let created = chrono::Utc::now().timestamp();
|
||||||
let created_str = format!("{}", created);
|
let created_str = format!("{}", created);
|
||||||
let hash = &proof.hash;
|
let hash = &proof.hash;
|
||||||
let expire = &format!("{}", utils::get_payment_threshold());
|
let expire = &format!("{}", utils::get_payment_threshold());
|
||||||
let message = &proof.message;
|
let message = &proof.message;
|
||||||
let signature = &proof.signature;
|
let signature = &proof.signature;
|
||||||
claims.insert("address", address);
|
claims.insert("subaddress", address);
|
||||||
claims.insert("created", &created_str);
|
claims.insert("created", &created_str);
|
||||||
claims.insert("hash", hash);
|
claims.insert("hash", hash);
|
||||||
claims.insert("expire", expire);
|
claims.insert("expire", expire);
|
||||||
|
@ -115,7 +116,7 @@ pub async fn prove_payment(contact: String, txp: &TxProof) -> Result<reqres::Jwp
|
||||||
///
|
///
|
||||||
/// is a JWP (JSON Web Proof) with the contents:
|
/// is a JWP (JSON Web Proof) with the contents:
|
||||||
///
|
///
|
||||||
/// `address`: this server's xmr address or subaddress
|
/// `subaddress`: a subaddress belonging to this nevmes instance
|
||||||
///
|
///
|
||||||
/// `created`: UTC timestamp the proof was created.
|
/// `created`: UTC timestamp the proof was created.
|
||||||
/// <i>Future use</i> Potential offline payments.
|
/// <i>Future use</i> Potential offline payments.
|
||||||
|
@ -146,8 +147,6 @@ impl<'r> FromRequest<'r> for PaymentProof {
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
let proof = request.headers().get_one("proof");
|
let proof = request.headers().get_one("proof");
|
||||||
let m_address: reqres::XmrRpcAddressResponse = monero::get_address().await;
|
|
||||||
let nevmes_address = m_address.result.address;
|
|
||||||
match proof {
|
match proof {
|
||||||
Some(proof) => {
|
Some(proof) => {
|
||||||
// check validity of address, payment amount and tx confirmations
|
// check validity of address, payment amount and tx confirmations
|
||||||
|
@ -160,8 +159,9 @@ impl<'r> FromRequest<'r> for PaymentProof {
|
||||||
return match jwp {
|
return match jwp {
|
||||||
Ok(j) => {
|
Ok(j) => {
|
||||||
let claims = j.claims();
|
let claims = j.claims();
|
||||||
let address = &claims["address"];
|
let subaddress = &claims["subaddress"];
|
||||||
if address != &nevmes_address {
|
let is_valid_subaddress = validate_subaddress(subaddress).await;
|
||||||
|
if !is_valid_subaddress {
|
||||||
return Outcome::Failure((
|
return Outcome::Failure((
|
||||||
Status::PaymentRequired,
|
Status::PaymentRequired,
|
||||||
PaymentProofError::Invalid,
|
PaymentProofError::Invalid,
|
||||||
|
@ -172,7 +172,7 @@ impl<'r> FromRequest<'r> for PaymentProof {
|
||||||
let signature = &claims["signature"];
|
let signature = &claims["signature"];
|
||||||
// verify proof
|
// verify proof
|
||||||
let txp: TxProof = TxProof {
|
let txp: TxProof = TxProof {
|
||||||
address: String::from(address),
|
subaddress: String::from(subaddress),
|
||||||
hash: String::from(hash),
|
hash: String::from(hash),
|
||||||
confirmations: 0,
|
confirmations: 0,
|
||||||
message: String::from(message),
|
message: String::from(message),
|
||||||
|
@ -224,7 +224,7 @@ async fn validate_proof(txp: &TxProof) -> TxProof {
|
||||||
&& p.result.confirmations < cth && p.result.received >= pth;
|
&& p.result.confirmations < cth && p.result.received >= pth;
|
||||||
if lgtm {
|
if lgtm {
|
||||||
return TxProof {
|
return TxProof {
|
||||||
address: String::from(&txp.address),
|
subaddress: String::from(&txp.subaddress),
|
||||||
hash: String::from(&txp.hash),
|
hash: String::from(&txp.hash),
|
||||||
confirmations: p.result.confirmations,
|
confirmations: p.result.confirmations,
|
||||||
message: String::from(&txp.message),
|
message: String::from(&txp.message),
|
||||||
|
@ -233,3 +233,16 @@ async fn validate_proof(txp: &TxProof) -> TxProof {
|
||||||
}
|
}
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate that the subaddress in the proof was
|
||||||
|
///
|
||||||
|
/// created by us. TODO(c2m): Store the index for quicker
|
||||||
|
///
|
||||||
|
/// lookups.
|
||||||
|
async fn validate_subaddress(subaddress: &String) -> bool {
|
||||||
|
let m_address = monero::get_address().await;
|
||||||
|
let all_address = m_address.result.addresses;
|
||||||
|
let mut address_list: Vec<String> = Vec::new();
|
||||||
|
for s_address in all_address { address_list.push(s_address.address); }
|
||||||
|
return address_list.contains(&subaddress);
|
||||||
|
}
|
||||||
|
|
|
@ -114,6 +114,11 @@ pub struct XmrRpcSweepAllParams {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub struct XmrRpcCreateAddressParams {
|
||||||
|
pub account_index: u8,
|
||||||
|
}
|
||||||
|
|
||||||
// requests
|
// requests
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct XmrRpcValidateAddressRequest {
|
pub struct XmrRpcValidateAddressRequest {
|
||||||
|
@ -242,6 +247,14 @@ pub struct XmrRpcSweepAllRequest {
|
||||||
pub params: XmrRpcSweepAllParams,
|
pub params: XmrRpcSweepAllParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub struct XmrRpcCreateAddressRequest {
|
||||||
|
pub jsonrpc: String,
|
||||||
|
pub id: String,
|
||||||
|
pub method: String,
|
||||||
|
pub params: XmrRpcCreateAddressParams,
|
||||||
|
}
|
||||||
|
|
||||||
// results
|
// results
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct XmrRpcValidateAddressResult {
|
pub struct XmrRpcValidateAddressResult {
|
||||||
|
@ -295,13 +308,13 @@ pub struct XmrRpcSignMultisigResult {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SubAddressInfo {
|
pub struct SubAddressInfo {
|
||||||
pub account_index: u8,
|
pub account_index: u64,
|
||||||
pub address_index: u8,
|
pub address_index: u64,
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub balance: u128,
|
pub balance: u128,
|
||||||
pub unlocked_balance: u128,
|
pub unlocked_balance: u128,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub num_unspent_outputs: u8,
|
pub num_unspent_outputs: u64,
|
||||||
pub time_to_unlock: u128,
|
pub time_to_unlock: u128,
|
||||||
pub blocks_to_unlock: u128,
|
pub blocks_to_unlock: u128,
|
||||||
}
|
}
|
||||||
|
@ -309,7 +322,7 @@ pub struct SubAddressInfo {
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
pub address: String,
|
pub address: String,
|
||||||
pub address_index: u8,
|
pub address_index: u64,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub used: bool,
|
pub used: bool,
|
||||||
}
|
}
|
||||||
|
@ -412,6 +425,14 @@ pub struct XmrRpcSweepAllResult {
|
||||||
pub weight_list: Vec<u128>,
|
pub weight_list: Vec<u128>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct XmrRpcCreateAddressResult {
|
||||||
|
pub address: String,
|
||||||
|
pub address_index: u64,
|
||||||
|
pub address_indices: Vec<u64>,
|
||||||
|
pub addresses: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct XmrDaemonGetInfoResult {
|
pub struct XmrDaemonGetInfoResult {
|
||||||
pub adjusted_time: u64,
|
pub adjusted_time: u64,
|
||||||
|
@ -786,6 +807,24 @@ impl Default for XmrRpcSweepAllResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct XmrRpcCreateAddressResponse {
|
||||||
|
pub result: XmrRpcCreateAddressResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for XmrRpcCreateAddressResponse {
|
||||||
|
fn default() -> Self {
|
||||||
|
XmrRpcCreateAddressResponse {
|
||||||
|
result: XmrRpcCreateAddressResult {
|
||||||
|
address: utils::empty_string(),
|
||||||
|
address_index: 0,
|
||||||
|
address_indices: Vec::new(),
|
||||||
|
addresses: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// END XMR Structs
|
// END XMR Structs
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@ fn send_payment_req(
|
||||||
let ptxp_hash = String::from(&transfer.result.tx_hash);
|
let ptxp_hash = String::from(&transfer.result.tx_hash);
|
||||||
let ftxp_hash = String::from(&transfer.result.tx_hash);
|
let ftxp_hash = String::from(&transfer.result.tx_hash);
|
||||||
let ptxp: proof::TxProof = proof::TxProof {
|
let ptxp: proof::TxProof = proof::TxProof {
|
||||||
address: ptxp_address,
|
subaddress: ptxp_address,
|
||||||
confirmations: 0,
|
confirmations: 0,
|
||||||
hash: ptxp_hash,
|
hash: ptxp_hash,
|
||||||
message: utils::empty_string(),
|
message: utils::empty_string(),
|
||||||
|
@ -664,7 +664,7 @@ fn send_payment_req(
|
||||||
let get_txp: reqres::XmrRpcGetTxProofResponse = monero::get_tx_proof(ptxp).await;
|
let get_txp: reqres::XmrRpcGetTxProofResponse = monero::get_tx_proof(ptxp).await;
|
||||||
// use the signature to create the FINALIZED transaction proof
|
// use the signature to create the FINALIZED transaction proof
|
||||||
let ftxp: proof::TxProof = proof::TxProof {
|
let ftxp: proof::TxProof = proof::TxProof {
|
||||||
address: ftxp_address,
|
subaddress: ftxp_address,
|
||||||
confirmations: 0,
|
confirmations: 0,
|
||||||
hash: ftxp_hash,
|
hash: ftxp_hash,
|
||||||
message: utils::empty_string(),
|
message: utils::empty_string(),
|
||||||
|
|
Loading…
Reference in a new issue