Wallet output selection performance (#238)

* allow selecting a commit while providing a key index
* added static reference to libsecp that can be called throughout
* don't serialise rangeproof to json if it's not desired
This commit is contained in:
Yeastplume 2017-11-07 16:48:37 +00:00 committed by Ignotus Peverell
parent 8f0373b81e
commit 48a60858ba
12 changed files with 96 additions and 23 deletions

View file

@ -58,7 +58,7 @@ impl UtxoHandler {
.get_block_header_by_output_commit(&commit)
.map_err(|_| Error::NotFound)?;
Ok(Output::from_output(&out, &header))
Ok(Output::from_output(&out, &header, false))
}
}
@ -138,10 +138,10 @@ impl Handler for SumTreeHandler {
}
}
match *path_elems.last().unwrap() {
"roots" => json_response(&self.get_roots()),
"lastutxos" => json_response(&self.get_last_n_utxo(last_n)),
"lastrangeproofs" => json_response(&self.get_last_n_rangeproof(last_n)),
"lastkernels" => json_response(&self.get_last_n_kernel(last_n)),
"roots" => json_response_pretty(&self.get_roots()),
"lastutxos" => json_response_pretty(&self.get_last_n_utxo(last_n)),
"lastrangeproofs" => json_response_pretty(&self.get_last_n_rangeproof(last_n)),
"lastkernels" => json_response_pretty(&self.get_last_n_kernel(last_n)),
_ => Ok(Response::with((status::BadRequest, ""))),
}
}
@ -154,7 +154,7 @@ pub struct PeersAllHandler {
impl Handler for PeersAllHandler {
fn handle(&self, _req: &mut Request) -> IronResult<Response> {
let peers = &self.peer_store.all_peers();
json_response(&peers)
json_response_pretty(&peers)
}
}
@ -266,12 +266,22 @@ fn json_response<T>(s: &T) -> IronResult<Response>
where
T: Serialize,
{
match serde_json::to_string_pretty(s) {
match serde_json::to_string(s) {
Ok(json) => Ok(Response::with((status::Ok, json))),
Err(_) => Ok(Response::with((status::InternalServerError, ""))),
}
}
// pretty-printed version of above
fn json_response_pretty<T>(s: &T) -> IronResult<Response>
where
T: Serialize,
{
match serde_json::to_string_pretty(s) {
Ok(json) => Ok(Response::with((status::Ok, json))),
Err(_) => Ok(Response::with((status::InternalServerError, ""))),
}
}
/// Start all server HTTP handlers. Register all of them with Iron
/// and runs the corresponding HTTP server.
pub fn start_rest_apis<T>(

View file

@ -138,7 +138,7 @@ pub struct Output {
/// The homomorphic commitment representing the output's amount
pub commit: pedersen::Commitment,
/// A proof that the commitment is in the right range
pub proof: pedersen::RangeProof,
pub proof: Option<pedersen::RangeProof>,
/// The height of the block creating this output
pub height: u64,
/// The lock height (earliest block this output can be spent)
@ -146,7 +146,7 @@ pub struct Output {
}
impl Output {
pub fn from_output(output: &core::Output, block_header: &core::BlockHeader) -> Output {
pub fn from_output(output: &core::Output, block_header: &core::BlockHeader, include_proof:bool) -> Output {
let (output_type, lock_height) = match output.features {
x if x.contains(core::transaction::COINBASE_OUTPUT) => (
OutputType::Coinbase,
@ -158,7 +158,10 @@ impl Output {
Output {
output_type: output_type,
commit: output.commit,
proof: output.proof,
proof: match include_proof {
true => Some(output.proof),
false => None,
},
height: block_header.height,
lock_height: lock_height,
}

View file

@ -20,7 +20,6 @@ use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use util::secp;
use util::secp::pedersen::{RangeProof, Commitment};
use core::core::{Block, Output, SumCommit, TxKernel};
@ -229,7 +228,6 @@ impl<'a> Extension<'a> {
/// applied in order of the provided Vec. If pruning is enabled, inputs also
/// prune MMR data.
pub fn apply_block(&mut self, b: &Block) -> Result<(), Error> {
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
// doing inputs first guarantees an input can't spend an output in the
// same block, enforcing block cut-through
@ -261,7 +259,6 @@ impl<'a> Extension<'a> {
.push(
SumCommit {
commit: out.commitment(),
secp: secp.clone(),
},
Some(out.switch_commit_hash()),
)

View file

@ -17,6 +17,7 @@
use byteorder::{BigEndian, ByteOrder};
use blake2::blake2b::blake2b;
use util::secp::{self, Message, Secp256k1, Signature};
use util::static_secp_instance;
use util::secp::pedersen::{Commitment, RangeProof};
use std::cmp::Ordering;
use std::ops;
@ -322,6 +323,7 @@ impl Transaction {
Ok(rsum)
}
/// Builds a transaction kernel
pub fn build_kernel(&self, excess: Commitment) -> TxKernel {
TxKernel {
features: DEFAULT_KERNEL,
@ -533,8 +535,6 @@ impl Output {
pub struct SumCommit {
/// Output commitment
pub commit: Commitment,
/// Secp256k1 used to sum
pub secp: Secp256k1,
}
/// Outputs get summed through their commitments.
@ -544,7 +544,6 @@ impl Summable for SumCommit {
fn sum(&self) -> SumCommit {
SumCommit {
commit: self.commit.clone(),
secp: self.secp.clone(),
}
}
@ -562,12 +561,10 @@ impl Writeable for SumCommit {
impl Readable for SumCommit {
fn read(reader: &mut Reader) -> Result<SumCommit, ser::Error> {
let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit);
let commit = Commitment::read(reader)?;
Ok(SumCommit {
commit: commit,
secp: secp,
})
}
}
@ -576,7 +573,8 @@ impl ops::Add for SumCommit {
type Output = SumCommit;
fn add(self, other: SumCommit) -> SumCommit {
let sum = match self.secp
let secp = static_secp_instance();
let sum = match secp.lock().unwrap()
.commit_sum(vec![self.commit.clone(), other.commit.clone()], vec![])
{
Ok(s) => s,
@ -584,7 +582,6 @@ impl ops::Add for SumCommit {
};
SumCommit {
commit: sum,
secp: self.secp,
}
}
}

View file

@ -7,6 +7,7 @@ authors = ["Antioch Peverell"]
byteorder = "~1"
blake2-rfc = "~0.2.17"
rand = "~0.3"
slog = { version = "^2.0.12", features = ["max_level_trace", "release_max_level_trace"] }
serde = "~1.0.8"
serde_derive = "~1.0.8"
serde_json = "~1.0.3"

View file

@ -134,7 +134,7 @@ impl Identifier {
}
fn from_hex(hex: &str) -> Result<Identifier, Error> {
let bytes = util::from_hex(hex.to_string())?;
let bytes = util::from_hex(hex.to_string()).unwrap();
Ok(Identifier::from_bytes(&bytes))
}

View file

@ -19,6 +19,7 @@ use util::secp;
use util::secp::{Message, Secp256k1, Signature};
use util::secp::key::SecretKey;
use util::secp::pedersen::{Commitment, ProofMessage, ProofInfo, RangeProof};
use util::logger::LOGGER;
use blake2;
use blind::{BlindSum, BlindingFactor};
use extkey::{self, Identifier};
@ -93,11 +94,18 @@ impl Keychain {
Ok(key_id)
}
fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error> {
fn derived_key_search(&self, key_id: &Identifier, n_child: Option<u32>) -> Result<SecretKey, Error> {
if let Some(key) = self.key_overrides.get(key_id) {
return Ok(*key);
}
trace!(LOGGER, "Derived Key key_id: {}", key_id);
if let Some(n) = n_child{
let extkey = self.extkey.derive(&self.secp, n)?;
return Ok(extkey.key);
};
for i in 1..10000 {
let extkey = self.extkey.derive(&self.secp, i)?;
if extkey.identifier(&self.secp)? == *key_id {
@ -109,12 +117,26 @@ impl Keychain {
))
}
fn derived_key(&self, key_id: &Identifier) -> Result<SecretKey, Error> {
self.derived_key_search(key_id, None)
}
fn derived_key_from_index(&self, key_id: &Identifier, n_child:u32) -> Result<SecretKey, Error> {
self.derived_key_search(key_id, Some(n_child))
}
pub fn commit(&self, amount: u64, key_id: &Identifier) -> Result<Commitment, Error> {
let skey = self.derived_key(key_id)?;
let commit = self.secp.commit(amount, skey)?;
Ok(commit)
}
pub fn commit_with_key_index(&self, amount: u64, key_id: &Identifier, n_child: u32) -> Result<Commitment, Error> {
let skey = self.derived_key_from_index(key_id, n_child)?;
let commit = self.secp.commit(amount, skey)?;
Ok(commit)
}
pub fn switch_commit(&self, key_id: &Identifier) -> Result<Commitment, Error> {
let skey = self.derived_key(key_id)?;
let commit = self.secp.switch_commit(skey)?;

View file

@ -22,6 +22,8 @@ extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate slog;
mod blind;
mod extkey;

View file

@ -10,6 +10,7 @@ slog = { version = "^2.0.12", features = ["max_level_trace", "release_max_level_
slog-term = "^2.2.0"
slog-async = "^2.1.0"
lazy_static = "~0.2.8"
rand = "0.3"
serde = "~1.0.8"
serde_derive = "~1.0.8"
secp256k1zkp = { git = "https://github.com/mimblewimble/rust-secp256k1-zkp", tag="grin_integration_1" }

View file

@ -26,6 +26,8 @@ extern crate slog;
extern crate slog_async;
extern crate slog_term;
extern crate rand;
#[macro_use]
extern crate lazy_static;
@ -41,6 +43,10 @@ pub use secp_ as secp;
pub mod logger;
pub use logger::{init_logger, init_test_logger, LOGGER};
// Static secp instance
pub mod secp_static;
pub use secp_static::static_secp_instance;
pub mod types;
pub use types::LoggingConfig;

34
util/src/secp_static.rs Normal file
View file

@ -0,0 +1,34 @@
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Globally accessible static instance of secp256k1, to avoid
//! initialisation overhead
use std::sync::{Arc, Mutex};
use rand::{thread_rng};
use secp_ as secp;
lazy_static! {
/// Static reference to secp instance
pub static ref SECP256K1:Arc<Mutex<secp::Secp256k1>>
= Arc::new(Mutex::new(secp::Secp256k1::with_caps(secp::ContextFlag::Commit)));
}
/// Returns the static instance, but calls randomize on it as well
/// (Recommended to avoid side channel attacks
pub fn static_secp_instance()-> Arc<Mutex<secp::Secp256k1>>{
let mut secp_inst=SECP256K1.lock().unwrap();
secp_inst.randomize(&mut thread_rng());
SECP256K1.clone()
}

View file

@ -67,7 +67,7 @@ pub fn refresh_outputs(config: &WalletConfig, keychain: &Keychain) -> Result<(),
.filter(|out| out.status != OutputStatus::Spent)
{
let key_id = keychain.derive_key_id(out.n_child).unwrap();
let commit = keychain.commit(out.value, &key_id).unwrap();
let commit = keychain.commit_with_key_index(out.value, &key_id, out.n_child).unwrap();
commits.push(commit);
wallet_outputs.insert(commit, out.key_id.clone());
}