mirror of
https://github.com/mimblewimble/mwixnet.git
synced 2025-01-21 03:21:09 +03:00
filter duplicate commitments
This commit is contained in:
parent
8cf41e4730
commit
bff59b08ac
4 changed files with 60 additions and 26 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -2089,6 +2089,15 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
|
checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
|
@ -2480,6 +2489,7 @@ dependencies = [
|
||||||
"grin_wallet_libwallet",
|
"grin_wallet_libwallet",
|
||||||
"hmac 0.12.0",
|
"hmac 0.12.0",
|
||||||
"hyper 0.14.14",
|
"hyper 0.14.14",
|
||||||
|
"itertools",
|
||||||
"jsonrpc-core 18.0.0",
|
"jsonrpc-core 18.0.0",
|
||||||
"jsonrpc-derive",
|
"jsonrpc-derive",
|
||||||
"jsonrpc-http-server",
|
"jsonrpc-http-server",
|
||||||
|
|
|
@ -15,6 +15,7 @@ failure = "0.1.8"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
hmac = { version = "0.12.0", features = ["std"]}
|
hmac = { version = "0.12.0", features = ["std"]}
|
||||||
hyper = { version = "0.14", features = ["full"] }
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
|
itertools = { version = "0.10.3"}
|
||||||
jsonrpc-core = "18.0"
|
jsonrpc-core = "18.0"
|
||||||
jsonrpc-derive = "18.0"
|
jsonrpc-derive = "18.0"
|
||||||
jsonrpc-http-server = "18.0"
|
jsonrpc-http-server = "18.0"
|
||||||
|
|
|
@ -24,6 +24,12 @@ pub trait GrinNode : Send + Sync {
|
||||||
fn post_tx(&self, tx: &Transaction) -> Result<()>;
|
fn post_tx(&self, tx: &Transaction) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a commitment is in the UTXO set
|
||||||
|
pub fn is_unspent(node: &Arc<dyn GrinNode>, commit: &Commitment) -> Result<bool> {
|
||||||
|
let utxo = node.get_utxo(&commit)?;
|
||||||
|
Ok(utxo.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether a commitment is spendable at the block height provided
|
/// Checks whether a commitment is spendable at the block height provided
|
||||||
pub fn is_spendable(node: &Arc<dyn GrinNode>, output_commit: &Commitment, next_block_height: u64) -> Result<bool> {
|
pub fn is_spendable(node: &Arc<dyn GrinNode>, output_commit: &Commitment, next_block_height: u64) -> Result<bool> {
|
||||||
let output = node.get_utxo(&output_commit)?;
|
let output = node.get_utxo(&output_commit)?;
|
||||||
|
|
|
@ -1,37 +1,40 @@
|
||||||
use crate::config::ServerConfig;
|
use crate::config::ServerConfig;
|
||||||
use crate::node::{self, GrinNode};
|
use crate::node::{self, GrinNode};
|
||||||
use crate::onion::Onion;
|
use crate::onion::Onion;
|
||||||
use crate::secp::{self, ComSignature, Secp256k1, SecretKey};
|
use crate::secp::{self, Commitment, ComSignature, RangeProof, Secp256k1, SecretKey};
|
||||||
use crate::wallet::{self, Wallet};
|
use crate::wallet::{self, Wallet};
|
||||||
|
|
||||||
use grin_core::core::{Input, Output, OutputFeatures, TransactionBody};
|
use grin_core::core::{Input, Output, OutputFeatures, TransactionBody};
|
||||||
use grin_core::global::DEFAULT_ACCEPT_FEE_BASE;
|
use grin_core::global::DEFAULT_ACCEPT_FEE_BASE;
|
||||||
use grin_util::StopState;
|
use grin_util::StopState;
|
||||||
|
use itertools::Itertools;
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use jsonrpc_http_server::*;
|
use jsonrpc_http_server::*;
|
||||||
use jsonrpc_http_server::jsonrpc_core::*;
|
use jsonrpc_http_server::jsonrpc_core::*;
|
||||||
use jsonrpc_core::{Result, Value};
|
use jsonrpc_core::{Result, Value};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Submission {
|
struct Submission {
|
||||||
pub excess: SecretKey,
|
excess: SecretKey,
|
||||||
pub output: Option<Output>,
|
output_commit: Commitment,
|
||||||
pub input: Input,
|
rangeproof: Option<RangeProof>,
|
||||||
pub fee: u64,
|
input: Input,
|
||||||
pub onion: Onion,
|
fee: u64,
|
||||||
|
onion: Onion,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct SwapReq {
|
pub struct SwapReq {
|
||||||
pub onion: Onion,
|
onion: Onion,
|
||||||
#[serde(with = "secp::comsig_serde")]
|
#[serde(with = "secp::comsig_serde")]
|
||||||
pub comsig: ComSignature,
|
comsig: ComSignature,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SERVER_STATE: Mutex<Vec<Submission>> = Mutex::new(Vec::new());
|
static ref SERVER_STATE: Mutex<HashMap<Commitment, Submission>> = Mutex::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rpc(server)]
|
#[rpc(server)]
|
||||||
|
@ -45,7 +48,7 @@ pub trait Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ServerImpl {
|
struct ServerImpl {
|
||||||
server_config: ServerConfig,
|
server_config: ServerConfig,
|
||||||
stop_state: Arc<StopState>,
|
stop_state: Arc<StopState>,
|
||||||
wallet: Arc<dyn Wallet>,
|
wallet: Arc<dyn Wallet>,
|
||||||
|
@ -53,7 +56,7 @@ pub struct ServerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerImpl {
|
impl ServerImpl {
|
||||||
pub fn new(server_config: ServerConfig, stop_state: Arc<StopState>, wallet: Arc<dyn Wallet>, node: Arc<dyn GrinNode>) -> Self {
|
fn new(server_config: ServerConfig, stop_state: Arc<StopState>, wallet: Arc<dyn Wallet>, node: Arc<dyn GrinNode>) -> Self {
|
||||||
ServerImpl { server_config, stop_state, wallet, node }
|
ServerImpl { server_config, stop_state, wallet, node }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,13 +75,16 @@ impl ServerImpl {
|
||||||
/// and assemble the coinswap transaction, posting the transaction to the configured node.
|
/// and assemble the coinswap transaction, posting the transaction to the configured node.
|
||||||
///
|
///
|
||||||
/// Currently only a single mix node is used. Milestone 3 will include support for multiple mix nodes.
|
/// Currently only a single mix node is used. Milestone 3 will include support for multiple mix nodes.
|
||||||
pub fn execute_round(&self) -> crate::error::Result<()> {
|
fn execute_round(&self) -> crate::error::Result<()> {
|
||||||
let mut locked_state = SERVER_STATE.lock().unwrap();
|
let mut locked_state = SERVER_STATE.lock().unwrap();
|
||||||
let next_block_height = self.node.get_chain_height()? + 1;
|
let next_block_height = self.node.get_chain_height()? + 1;
|
||||||
|
|
||||||
let spendable : Vec<Submission> = locked_state
|
let spendable : Vec<Submission> = locked_state
|
||||||
.iter()
|
.values()
|
||||||
|
.into_iter()
|
||||||
|
.unique_by(|s| s.output_commit)
|
||||||
.filter(|s| node::is_spendable(&self.node, &s.input.commit, next_block_height).unwrap_or(false))
|
.filter(|s| node::is_spendable(&self.node, &s.input.commit, next_block_height).unwrap_or(false))
|
||||||
|
.filter(|s| !node::is_unspent(&self.node, &s.output_commit).unwrap_or(true))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -101,7 +107,7 @@ impl ServerImpl {
|
||||||
let outputs : Vec<Output> = spendable
|
let outputs : Vec<Output> = spendable
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(_, s)| s.output)
|
.map(|(_, s)| Output::new(OutputFeatures::Plain, s.output_commit, s.rangeproof.unwrap()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let excesses : Vec<SecretKey> = spendable
|
let excesses : Vec<SecretKey> = spendable
|
||||||
|
@ -122,6 +128,11 @@ impl ServerImpl {
|
||||||
impl Server for ServerImpl {
|
impl Server for ServerImpl {
|
||||||
/// Implements the 'swap' API
|
/// Implements the 'swap' API
|
||||||
fn swap(&self, swap: SwapReq) -> Result<Value> {
|
fn swap(&self, swap: SwapReq) -> Result<Value> {
|
||||||
|
// milestone 3: check that enc_payloads length matches number of configured servers
|
||||||
|
if swap.onion.enc_payloads.len() != 1 {
|
||||||
|
return Err(jsonrpc_core::Error::invalid_params("Multi server not supported until milestone 3"));
|
||||||
|
}
|
||||||
|
|
||||||
// Verify commitment signature to ensure caller owns the output
|
// Verify commitment signature to ensure caller owns the output
|
||||||
let serialized_onion = swap.onion.serialize()
|
let serialized_onion = swap.onion.serialize()
|
||||||
.map_err(|_| jsonrpc_core::Error::internal_error())?;
|
.map_err(|_| jsonrpc_core::Error::internal_error())?;
|
||||||
|
@ -149,18 +160,24 @@ impl Server for ServerImpl {
|
||||||
.map_err(|_| jsonrpc_core::Error::internal_error())?;
|
.map_err(|_| jsonrpc_core::Error::internal_error())?;
|
||||||
|
|
||||||
// Verify the bullet proof and build the final output
|
// Verify the bullet proof and build the final output
|
||||||
let output = match peeled.0.rangeproof {
|
if let Some(r) = peeled.0.rangeproof {
|
||||||
Some(r) => {
|
|
||||||
let secp = Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit);
|
let secp = Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit);
|
||||||
secp.verify_bullet_proof(output_commit, r, None)
|
secp.verify_bullet_proof(output_commit, r, None)
|
||||||
.map_err(|_| jsonrpc_core::Error::invalid_params("RangeProof invalid"))?;
|
.map_err(|_| jsonrpc_core::Error::invalid_params("RangeProof invalid"))?;
|
||||||
Some(Output::new(OutputFeatures::Plain, output_commit, r))
|
} else {
|
||||||
},
|
// milestone 3: only the last hop will have a rangeproof
|
||||||
None => None
|
return Err(jsonrpc_core::Error::invalid_params("Rangeproof expected"));
|
||||||
};
|
}
|
||||||
SERVER_STATE.lock().unwrap().push(Submission{
|
|
||||||
|
let mut locked = SERVER_STATE.lock().unwrap();
|
||||||
|
if locked.contains_key(&swap.onion.commit) {
|
||||||
|
return Err(jsonrpc_core::Error::invalid_params("swap already called for coin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
locked.insert(swap.onion.commit, Submission{
|
||||||
excess: peeled.0.excess,
|
excess: peeled.0.excess,
|
||||||
output: output,
|
output_commit: output_commit,
|
||||||
|
rangeproof: peeled.0.rangeproof,
|
||||||
input: input,
|
input: input,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
onion: peeled.1
|
onion: peeled.1
|
||||||
|
|
Loading…
Reference in a new issue