mirror of
https://github.com/mimblewimble/grin.git
synced 2025-05-01 06:41:15 +03:00
libwallet refactor context, aggsig, error handling (#1087)
* remove context object from aggsig and transaction libs * fix to aggsig, and remove unnecessary warnings * put tx_fee function into libwallet::transaction * Error cleanup, and creating libwallet error type * remove some unwraps * checker bug * ensure transaction tests checks sender's wallet
This commit is contained in:
parent
ff5d651b6f
commit
1f94bfc038
27 changed files with 538 additions and 623 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -805,7 +805,6 @@ dependencies = [
|
||||||
"grin_util 0.2.0",
|
"grin_util 0.2.0",
|
||||||
"hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"prettytable-rs 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"prettytable-rs 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"router 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"router 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
@ -23,11 +23,11 @@ extern crate grin_wallet as wallet;
|
||||||
|
|
||||||
extern crate blake2_rfc as blake2;
|
extern crate blake2_rfc as blake2;
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
use std::time;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread;
|
||||||
|
use std::time;
|
||||||
|
|
||||||
use wallet::WalletConfig;
|
use wallet::WalletConfig;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ failure_derive = "0.1"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
hyper = "0.11"
|
hyper = "0.11"
|
||||||
iron = "0.5"
|
iron = "0.5"
|
||||||
lazy_static = "0.2"
|
|
||||||
prettytable-rs = "0.6"
|
prettytable-rs = "0.6"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
router = "0.5"
|
router = "0.5"
|
||||||
|
|
|
@ -226,6 +226,9 @@ fn refresh_output_state(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_old_unconfirmed(config: &WalletConfig, tip: &api::Tip) -> Result<(), Error> {
|
fn clean_old_unconfirmed(config: &WalletConfig, tip: &api::Tip) -> Result<(), Error> {
|
||||||
|
if tip.height < 500 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||||
wallet_data.outputs.retain(|_, ref mut out| {
|
wallet_data.outputs.retain(|_, ref mut out| {
|
||||||
!(out.status == OutputStatus::Unconfirmed && out.height > 0
|
!(out.status == OutputStatus::Unconfirmed && out.height > 0
|
||||||
|
|
|
@ -13,17 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Grin Wallet specific key management functions
|
//! Grin Wallet specific key management functions
|
||||||
use rand::thread_rng;
|
use keychain::{Identifier, Keychain};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use core::core::{amount_to_hr_string, Committed, Transaction};
|
|
||||||
use failure::ResultExt;
|
|
||||||
use keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
|
|
||||||
use libwallet::{aggsig, build};
|
|
||||||
use types::*;
|
use types::*;
|
||||||
use util::secp::Signature;
|
|
||||||
use util::secp::key::{PublicKey, SecretKey};
|
|
||||||
use util::{secp, LOGGER};
|
|
||||||
|
|
||||||
/// Get our next available key
|
/// Get our next available key
|
||||||
pub fn new_output_key(
|
pub fn new_output_key(
|
||||||
|
|
|
@ -25,5 +25,6 @@
|
||||||
#![deny(unused_mut)]
|
#![deny(unused_mut)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod selection;
|
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
|
pub mod selection;
|
||||||
|
pub mod sigcontext;
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
//! Selection of inputs for building transactions
|
//! Selection of inputs for building transactions
|
||||||
|
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use grinwallet::keys;
|
use grinwallet::{keys, sigcontext};
|
||||||
use keychain::{Identifier, Keychain};
|
use keychain::{Identifier, Keychain};
|
||||||
use libwallet::{aggsig, build, transaction};
|
use libwallet::{build, transaction};
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
/// Initialise a transaction on the sender side, returns a corresponding
|
/// Initialise a transaction on the sender side, returns a corresponding
|
||||||
|
@ -28,7 +28,6 @@ use types::*;
|
||||||
pub fn build_send_tx_slate(
|
pub fn build_send_tx_slate(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
|
||||||
num_participants: usize,
|
num_participants: usize,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
current_height: u64,
|
current_height: u64,
|
||||||
|
@ -36,7 +35,14 @@ pub fn build_send_tx_slate(
|
||||||
lock_height: u64,
|
lock_height: u64,
|
||||||
max_outputs: usize,
|
max_outputs: usize,
|
||||||
selection_strategy_is_use_all: bool,
|
selection_strategy_is_use_all: bool,
|
||||||
) -> Result<(transaction::Slate, impl FnOnce() -> Result<(), Error>), Error> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
transaction::Slate,
|
||||||
|
sigcontext::Context,
|
||||||
|
impl FnOnce() -> Result<(), Error>,
|
||||||
|
),
|
||||||
|
Error,
|
||||||
|
> {
|
||||||
let (elems, inputs, change_id, amount, fee) = select_send_tx(
|
let (elems, inputs, change_id, amount, fee) = select_send_tx(
|
||||||
config,
|
config,
|
||||||
keychain,
|
keychain,
|
||||||
|
@ -55,11 +61,12 @@ pub fn build_send_tx_slate(
|
||||||
slate.lock_height = lock_height;
|
slate.lock_height = lock_height;
|
||||||
slate.fee = fee;
|
slate.fee = fee;
|
||||||
|
|
||||||
let blinding = slate.add_transaction_elements(keychain, elems)?;
|
let blinding = slate
|
||||||
|
.add_transaction_elements(keychain, elems)
|
||||||
|
.context(ErrorKind::LibWalletError)?;
|
||||||
// Create our own private context
|
// Create our own private context
|
||||||
let mut context = context_manager.create_context(
|
let mut context = sigcontext::Context::new(
|
||||||
keychain.secp(),
|
keychain.secp(),
|
||||||
&slate.id,
|
|
||||||
blinding.secret_key(keychain.secp()).unwrap(),
|
blinding.secret_key(keychain.secp()).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -93,9 +100,7 @@ pub fn build_send_tx_slate(
|
||||||
}*/ })
|
}*/ })
|
||||||
};
|
};
|
||||||
|
|
||||||
context_manager.save_context(context);
|
Ok((slate, context, update_sender_wallet_fn))
|
||||||
|
|
||||||
Ok((slate, update_sender_wallet_fn))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new output in the wallet for the recipient,
|
/// Creates a new output in the wallet for the recipient,
|
||||||
|
@ -105,9 +110,15 @@ pub fn build_send_tx_slate(
|
||||||
pub fn build_recipient_output_with_slate(
|
pub fn build_recipient_output_with_slate(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
|
||||||
slate: &mut transaction::Slate,
|
slate: &mut transaction::Slate,
|
||||||
) -> Result<(Identifier, impl FnOnce() -> Result<(), Error>), Error> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Identifier,
|
||||||
|
sigcontext::Context,
|
||||||
|
impl FnOnce() -> Result<(), Error>,
|
||||||
|
),
|
||||||
|
Error,
|
||||||
|
> {
|
||||||
// Create a potential output for this transaction
|
// Create a potential output for this transaction
|
||||||
let (key_id, derivation) = keys::new_output_key(config, keychain)?;
|
let (key_id, derivation) = keys::new_output_key(config, keychain)?;
|
||||||
|
|
||||||
|
@ -115,14 +126,15 @@ pub fn build_recipient_output_with_slate(
|
||||||
let root_key_id = keychain.root_key_id();
|
let root_key_id = keychain.root_key_id();
|
||||||
let key_id_inner = key_id.clone();
|
let key_id_inner = key_id.clone();
|
||||||
let amount = slate.amount;
|
let amount = slate.amount;
|
||||||
|
let height = slate.height;
|
||||||
|
|
||||||
let blinding =
|
let blinding = slate
|
||||||
slate.add_transaction_elements(keychain, vec![build::output(amount, key_id.clone())])?;
|
.add_transaction_elements(keychain, vec![build::output(amount, key_id.clone())])
|
||||||
|
.context(ErrorKind::LibWalletError)?;
|
||||||
|
|
||||||
// Add blinding sum to our context
|
// Add blinding sum to our context
|
||||||
let mut context = context_manager.create_context(
|
let mut context = sigcontext::Context::new(
|
||||||
keychain.secp(),
|
keychain.secp(),
|
||||||
&slate.id,
|
|
||||||
blinding.secret_key(keychain.secp()).unwrap(),
|
blinding.secret_key(keychain.secp()).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -138,7 +150,7 @@ pub fn build_recipient_output_with_slate(
|
||||||
n_child: derivation,
|
n_child: derivation,
|
||||||
value: amount,
|
value: amount,
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
is_coinbase: false,
|
is_coinbase: false,
|
||||||
block: None,
|
block: None,
|
||||||
|
@ -146,8 +158,7 @@ pub fn build_recipient_output_with_slate(
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
context_manager.save_context(context);
|
Ok((key_id, context, wallet_add_fn))
|
||||||
Ok((key_id, wallet_add_fn))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a transaction to send to someone from the HD seed associated with the
|
/// Builds a transaction to send to someone from the HD seed associated with the
|
||||||
|
@ -203,7 +214,7 @@ pub fn select_send_tx(
|
||||||
// sender
|
// sender
|
||||||
let mut fee;
|
let mut fee;
|
||||||
// First attempt to spend without change
|
// First attempt to spend without change
|
||||||
fee = tx_fee(coins.len(), 1, coins_proof_count(&coins), None);
|
fee = transaction::tx_fee(coins.len(), 1, coins_proof_count(&coins), None);
|
||||||
let mut total: u64 = coins.iter().map(|c| c.value).sum();
|
let mut total: u64 = coins.iter().map(|c| c.value).sum();
|
||||||
let mut amount_with_fee = amount + fee;
|
let mut amount_with_fee = amount + fee;
|
||||||
|
|
||||||
|
@ -213,7 +224,7 @@ pub fn select_send_tx(
|
||||||
|
|
||||||
// Check if we need to use a change address
|
// Check if we need to use a change address
|
||||||
if total > amount_with_fee {
|
if total > amount_with_fee {
|
||||||
fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None);
|
fee = transaction::tx_fee(coins.len(), 2, coins_proof_count(&coins), None);
|
||||||
amount_with_fee = amount + fee;
|
amount_with_fee = amount + fee;
|
||||||
|
|
||||||
// Here check if we have enough outputs for the amount including fee otherwise
|
// Here check if we have enough outputs for the amount including fee otherwise
|
||||||
|
@ -235,14 +246,15 @@ pub fn select_send_tx(
|
||||||
selection_strategy_is_use_all,
|
selection_strategy_is_use_all,
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
fee = tx_fee(coins.len(), 2, coins_proof_count(&coins), None);
|
fee = transaction::tx_fee(coins.len(), 2, coins_proof_count(&coins), None);
|
||||||
total = coins.iter().map(|c| c.value).sum();
|
total = coins.iter().map(|c| c.value).sum();
|
||||||
amount_with_fee = amount + fee;
|
amount_with_fee = amount + fee;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build transaction skeleton with inputs and change
|
// build transaction skeleton with inputs and change
|
||||||
let (mut parts, change_key) = inputs_and_change(&coins, config, keychain, amount, fee)?;
|
let (mut parts, change_key) =
|
||||||
|
inputs_and_change(&coins, config, keychain, current_height, amount, fee)?;
|
||||||
|
|
||||||
// This is more proof of concept than anything but here we set lock_height
|
// This is more proof of concept than anything but here we set lock_height
|
||||||
// on tx being sent (based on current chain height via api).
|
// on tx being sent (based on current chain height via api).
|
||||||
|
@ -261,6 +273,7 @@ pub fn inputs_and_change(
|
||||||
coins: &Vec<OutputData>,
|
coins: &Vec<OutputData>,
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
|
height: u64,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
) -> Result<(Vec<Box<build::Append>>, Option<Identifier>), Error> {
|
) -> Result<(Vec<Box<build::Append>>, Option<Identifier>), Error> {
|
||||||
|
@ -310,7 +323,7 @@ pub fn inputs_and_change(
|
||||||
n_child: change_derivation,
|
n_child: change_derivation,
|
||||||
value: change as u64,
|
value: change as u64,
|
||||||
status: OutputStatus::Unconfirmed,
|
status: OutputStatus::Unconfirmed,
|
||||||
height: 0,
|
height: height,
|
||||||
lock_height: 0,
|
lock_height: 0,
|
||||||
is_coinbase: false,
|
is_coinbase: false,
|
||||||
block: None,
|
block: None,
|
||||||
|
|
84
wallet/src/grinwallet/sigcontext.rs
Normal file
84
wallet/src/grinwallet/sigcontext.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// 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.
|
||||||
|
//! Signature context holder helper (may be removed or replaced eventually)
|
||||||
|
use keychain::extkey::Identifier;
|
||||||
|
use libwallet::aggsig;
|
||||||
|
use util::secp::key::{PublicKey, SecretKey};
|
||||||
|
use util::secp::{self, Secp256k1};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
/// Holds the context for a single aggsig transaction
|
||||||
|
pub struct Context {
|
||||||
|
/// Secret key (of which public is shared)
|
||||||
|
pub sec_key: SecretKey,
|
||||||
|
/// Secret nonce (of which public is shared)
|
||||||
|
/// (basically a SecretKey)
|
||||||
|
pub sec_nonce: SecretKey,
|
||||||
|
/// store my outputs between invocations
|
||||||
|
pub output_ids: Vec<Identifier>,
|
||||||
|
/// store my inputs
|
||||||
|
pub input_ids: Vec<Identifier>,
|
||||||
|
/// store the calculated fee
|
||||||
|
pub fee: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Create a new context with defaults
|
||||||
|
pub fn new(secp: &secp::Secp256k1, sec_key: SecretKey) -> Context {
|
||||||
|
Context {
|
||||||
|
sec_key: sec_key,
|
||||||
|
sec_nonce: aggsig::create_secnonce(secp).unwrap(),
|
||||||
|
input_ids: vec![],
|
||||||
|
output_ids: vec![],
|
||||||
|
fee: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
/// Tracks an output contributing to my excess value (if it needs to
|
||||||
|
/// be kept between invocations
|
||||||
|
pub fn add_output(&mut self, output_id: &Identifier) {
|
||||||
|
self.output_ids.push(output_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all stored outputs
|
||||||
|
pub fn get_outputs(&self) -> Vec<Identifier> {
|
||||||
|
self.output_ids.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tracks IDs of my inputs into the transaction
|
||||||
|
/// be kept between invocations
|
||||||
|
pub fn add_input(&mut self, input_id: &Identifier) {
|
||||||
|
self.input_ids.push(input_id.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all stored input identifiers
|
||||||
|
pub fn get_inputs(&self) -> Vec<Identifier> {
|
||||||
|
self.input_ids.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns private key, private nonce
|
||||||
|
pub fn get_private_keys(&self) -> (SecretKey, SecretKey) {
|
||||||
|
(self.sec_key.clone(), self.sec_nonce.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns public key, public nonce
|
||||||
|
pub fn get_public_keys(&self, secp: &Secp256k1) -> (PublicKey, PublicKey) {
|
||||||
|
(
|
||||||
|
PublicKey::from_secret_key(secp, &self.sec_key).unwrap(),
|
||||||
|
PublicKey::from_secret_key(secp, &self.sec_nonce).unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,10 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use checker;
|
use checker;
|
||||||
use keychain::Keychain;
|
|
||||||
use core::core::amount_to_hr_string;
|
use core::core::amount_to_hr_string;
|
||||||
use types::{OutputStatus, WalletConfig, WalletData, WalletInfo};
|
use keychain::Keychain;
|
||||||
use prettytable;
|
use prettytable;
|
||||||
|
use types::{OutputStatus, WalletConfig, WalletData, WalletInfo};
|
||||||
|
|
||||||
pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
||||||
let wallet_info = retrieve_info(config, keychain);
|
let wallet_info = retrieve_info(config, keychain);
|
||||||
|
|
|
@ -41,31 +41,28 @@ extern crate router;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
extern crate tokio_retry;
|
extern crate tokio_retry;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate lazy_static;
|
|
||||||
|
|
||||||
extern crate grin_api as api;
|
extern crate grin_api as api;
|
||||||
extern crate grin_core as core;
|
extern crate grin_core as core;
|
||||||
extern crate grin_keychain as keychain;
|
extern crate grin_keychain as keychain;
|
||||||
extern crate grin_util as util;
|
extern crate grin_util as util;
|
||||||
|
|
||||||
pub mod checker;
|
pub mod checker;
|
||||||
mod handlers;
|
|
||||||
mod outputs;
|
|
||||||
mod info;
|
|
||||||
pub mod receiver;
|
|
||||||
mod sender;
|
|
||||||
pub mod types;
|
|
||||||
mod restore;
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod server;
|
|
||||||
pub mod libwallet;
|
|
||||||
pub mod grinwallet;
|
pub mod grinwallet;
|
||||||
|
mod handlers;
|
||||||
|
mod info;
|
||||||
|
pub mod libwallet;
|
||||||
|
mod outputs;
|
||||||
|
pub mod receiver;
|
||||||
|
mod restore;
|
||||||
|
mod sender;
|
||||||
|
pub mod server;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
pub use outputs::show_outputs;
|
|
||||||
pub use info::{retrieve_info, show_info};
|
pub use info::{retrieve_info, show_info};
|
||||||
|
pub use outputs::show_outputs;
|
||||||
pub use receiver::WalletReceiver;
|
pub use receiver::WalletReceiver;
|
||||||
|
pub use restore::restore;
|
||||||
pub use sender::{issue_burn_tx, issue_send_tx};
|
pub use sender::{issue_burn_tx, issue_send_tx};
|
||||||
pub use types::{BlockFees, CbData, Error, ErrorKind, WalletConfig, WalletInfo,
|
pub use types::{BlockFees, CbData, Error, ErrorKind, WalletConfig, WalletInfo,
|
||||||
WalletReceiveRequest, WalletSeed};
|
WalletReceiveRequest, WalletSeed};
|
||||||
pub use restore::restore;
|
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
/// Aggsig library definitions
|
//! Aggsig helper functions used in transaction creation.. should be only
|
||||||
use std::collections::HashMap;
|
//! interface into the underlying secp library
|
||||||
|
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use keychain::blind::BlindingFactor;
|
use keychain::blind::BlindingFactor;
|
||||||
use keychain::extkey::Identifier;
|
use keychain::extkey::Identifier;
|
||||||
|
@ -22,253 +21,38 @@ use util::kernel_sig_msg;
|
||||||
use util::secp::key::{PublicKey, SecretKey};
|
use util::secp::key::{PublicKey, SecretKey};
|
||||||
use util::secp::pedersen::Commitment;
|
use util::secp::pedersen::Commitment;
|
||||||
use util::secp::{self, aggsig, Message, Secp256k1, Signature};
|
use util::secp::{self, aggsig, Message, Secp256k1, Signature};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// exports a secure nonce guaranteed to be usable
|
||||||
/// Holds the context for a single aggsig transaction
|
/// in aggsig creation
|
||||||
pub struct Context {
|
pub fn create_secnonce(secp: &Secp256k1) -> Result<SecretKey, Error> {
|
||||||
/// Transaction ID
|
let nonce = aggsig::export_secnonce_single(secp)?;
|
||||||
pub transaction_id: Uuid,
|
Ok(nonce)
|
||||||
/// Secret key (of which public is shared)
|
|
||||||
pub sec_key: SecretKey,
|
|
||||||
/// Secret nonce (of which public is shared)
|
|
||||||
/// (basically a SecretKey)
|
|
||||||
pub sec_nonce: SecretKey,
|
|
||||||
/// If I'm the sender, store change key
|
|
||||||
/// TODO: remove in favor of outputs below
|
|
||||||
pub change_key: Option<Identifier>,
|
|
||||||
/// store my outputs between invocations
|
|
||||||
pub output_ids: Vec<Identifier>,
|
|
||||||
/// store my inputs
|
|
||||||
pub input_ids: Vec<Identifier>,
|
|
||||||
/// store the calculated fee
|
|
||||||
pub fee: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*impl Context {
|
/// Calculate a partial sig
|
||||||
/// Create a new context with defaults
|
pub fn calculate_partial_sig(
|
||||||
pub fn new(
|
secp: &Secp256k1,
|
||||||
secp: &secp::Secp256k1,
|
sec_key: &SecretKey,
|
||||||
sec_key: SecretKey,
|
sec_nonce: &SecretKey,
|
||||||
) -> Context {
|
nonce_sum: &PublicKey,
|
||||||
Context {
|
fee: u64,
|
||||||
sec_key: sec_key,
|
lock_height: u64,
|
||||||
sec_nonce: aggsig::export_secnonce_single(secp).unwrap(),
|
) -> Result<Signature, Error> {
|
||||||
change_key: None,
|
// Add public nonces kR*G + kS*G
|
||||||
input_ids: vec![],
|
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?;
|
||||||
output_ids: vec![],
|
|
||||||
fee: 0,
|
|
||||||
},
|
|
||||||
}*/
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
//Now calculate signature using message M=fee, nonce in e=nonce_sum
|
||||||
/// Holds many contexts, to support multiple transactions hitting a wallet
|
let sig = aggsig::sign_single(
|
||||||
/// receiver at once
|
secp,
|
||||||
/// TODO: Remove context manager in favour of context.. keeping multiple
|
&msg,
|
||||||
/// transactions separate is a wallet-specific concern
|
sec_key,
|
||||||
pub struct ContextManager {
|
Some(sec_nonce),
|
||||||
contexts: HashMap<Uuid, Context>,
|
Some(nonce_sum),
|
||||||
|
Some(nonce_sum),
|
||||||
|
)?;
|
||||||
|
Ok(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextManager {
|
|
||||||
/// Create
|
|
||||||
pub fn new() -> ContextManager {
|
|
||||||
ContextManager {
|
|
||||||
contexts: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a context for a transaction id if required
|
|
||||||
/// otherwise does nothing
|
|
||||||
pub fn create_context(
|
|
||||||
&mut self,
|
|
||||||
secp: &secp::Secp256k1,
|
|
||||||
transaction_id: &Uuid,
|
|
||||||
sec_key: SecretKey,
|
|
||||||
) -> Context {
|
|
||||||
if !self.contexts.contains_key(transaction_id) {
|
|
||||||
self.contexts.insert(
|
|
||||||
transaction_id.clone(),
|
|
||||||
Context {
|
|
||||||
sec_key: sec_key,
|
|
||||||
transaction_id: transaction_id.clone(),
|
|
||||||
sec_nonce: aggsig::export_secnonce_single(secp).unwrap(),
|
|
||||||
change_key: None,
|
|
||||||
input_ids: vec![],
|
|
||||||
output_ids: vec![],
|
|
||||||
fee: 0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.get_context(transaction_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a context by transaction id
|
|
||||||
pub fn get_context(&self, transaction_id: &Uuid) -> Context {
|
|
||||||
self.contexts.get(&transaction_id).unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Save context
|
|
||||||
pub fn save_context(&mut self, c: Context) {
|
|
||||||
self.contexts.insert(c.transaction_id.clone(), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
/// Tracks an output contributing to my excess value (if it needs to
|
|
||||||
/// be kept between invocations
|
|
||||||
pub fn add_output(&mut self, output_id: &Identifier) {
|
|
||||||
self.output_ids.push(output_id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all stored outputs
|
|
||||||
pub fn get_outputs(&self) -> Vec<Identifier> {
|
|
||||||
self.output_ids.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tracks IDs of my inputs into the transaction
|
|
||||||
/// be kept between invocations
|
|
||||||
pub fn add_input(&mut self, input_id: &Identifier) {
|
|
||||||
self.input_ids.push(input_id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all stored input identifiers
|
|
||||||
pub fn get_inputs(&self) -> Vec<Identifier> {
|
|
||||||
self.input_ids.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns private key, private nonce
|
|
||||||
pub fn get_private_keys(&self) -> (SecretKey, SecretKey) {
|
|
||||||
(self.sec_key.clone(), self.sec_nonce.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns public key, public nonce
|
|
||||||
pub fn get_public_keys(&self, secp: &Secp256k1) -> (PublicKey, PublicKey) {
|
|
||||||
(
|
|
||||||
PublicKey::from_secret_key(secp, &self.sec_key).unwrap(),
|
|
||||||
PublicKey::from_secret_key(secp, &self.sec_nonce).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Note 'secnonce' here is used to perform the signature, while 'pubnonce'
|
|
||||||
/// just allows you to provide a custom public nonce to include while
|
|
||||||
/// calculating e nonce_sum is the sum used to decide whether secnonce
|
|
||||||
/// should be inverted during sig time
|
|
||||||
pub fn sign_single(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
msg: &Message,
|
|
||||||
secnonce: Option<&SecretKey>,
|
|
||||||
pubnonce: Option<&PublicKey>,
|
|
||||||
nonce_sum: Option<&PublicKey>,
|
|
||||||
) -> Result<Signature, Error> {
|
|
||||||
let sig = aggsig::sign_single(secp, msg, &self.sec_key, secnonce, pubnonce, nonce_sum)?;
|
|
||||||
Ok(sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Verifies other final sig corresponds with what we're expecting
|
|
||||||
pub fn verify_final_sig_build_msg(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
sig: &Signature,
|
|
||||||
pubkey: &PublicKey,
|
|
||||||
fee: u64,
|
|
||||||
lock_height: u64,
|
|
||||||
) -> bool {
|
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap();
|
|
||||||
verify_single(secp, sig, &msg, None, pubkey, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Verifies other party's sig corresponds with what we're expecting
|
|
||||||
pub fn verify_partial_sig(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
sig: &Signature,
|
|
||||||
other_pub_nonce: &PublicKey,
|
|
||||||
pubkey: &PublicKey,
|
|
||||||
fee: u64,
|
|
||||||
lock_height: u64,
|
|
||||||
) -> bool {
|
|
||||||
let (_, sec_nonce) = self.get_private_keys();
|
|
||||||
let mut nonce_sum = other_pub_nonce.clone();
|
|
||||||
let _ = nonce_sum.add_exp_assign(secp, &sec_nonce);
|
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap();
|
|
||||||
|
|
||||||
verify_single(secp, sig, &msg, Some(&nonce_sum), pubkey, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
///TODO: Remove when below is integrated
|
|
||||||
pub fn calculate_partial_sig(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
other_pub_nonce: &PublicKey,
|
|
||||||
fee: u64,
|
|
||||||
lock_height: u64,
|
|
||||||
) -> Result<Signature, Error> {
|
|
||||||
// Add public nonces kR*G + kS*G
|
|
||||||
let (_, sec_nonce) = self.get_private_keys();
|
|
||||||
let mut nonce_sum = other_pub_nonce.clone();
|
|
||||||
let _ = nonce_sum.add_exp_assign(secp, &sec_nonce);
|
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?;
|
|
||||||
|
|
||||||
//Now calculate signature using message M=fee, nonce in e=nonce_sum
|
|
||||||
self.sign_single(
|
|
||||||
secp,
|
|
||||||
&msg,
|
|
||||||
Some(&sec_nonce),
|
|
||||||
Some(&nonce_sum),
|
|
||||||
Some(&nonce_sum),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calculate_partial_sig_with_nonce_sum(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
nonce_sum: &PublicKey,
|
|
||||||
fee: u64,
|
|
||||||
lock_height: u64,
|
|
||||||
) -> Result<Signature, Error> {
|
|
||||||
// Add public nonces kR*G + kS*G
|
|
||||||
let (_, sec_nonce) = self.get_private_keys();
|
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?;
|
|
||||||
|
|
||||||
//Now calculate signature using message M=fee, nonce in e=nonce_sum
|
|
||||||
self.sign_single(
|
|
||||||
secp,
|
|
||||||
&msg,
|
|
||||||
Some(&sec_nonce),
|
|
||||||
Some(&nonce_sum),
|
|
||||||
Some(&nonce_sum),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper function to calculate final signature
|
|
||||||
pub fn calculate_final_sig(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
part_sigs: Vec<&Signature>,
|
|
||||||
nonce_sum: &PublicKey,
|
|
||||||
) -> Result<Signature, Error> {
|
|
||||||
// Add public nonces kR*G + kS*G
|
|
||||||
let sig = aggsig::add_signatures_single(&secp, part_sigs, &nonce_sum)?;
|
|
||||||
Ok(sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper function to calculate final public key
|
|
||||||
pub fn calculate_final_pubkey(
|
|
||||||
&self,
|
|
||||||
secp: &Secp256k1,
|
|
||||||
their_public_key: &PublicKey,
|
|
||||||
) -> Result<PublicKey, Error> {
|
|
||||||
let (our_sec_key, _) = self.get_private_keys();
|
|
||||||
let mut pk_sum = their_public_key.clone();
|
|
||||||
let _ = pk_sum.add_exp_assign(secp, &our_sec_key);
|
|
||||||
Ok(pk_sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contextless functions
|
|
||||||
|
|
||||||
/// Verifies a partial sig given all public nonces used in the round
|
/// Verifies a partial sig given all public nonces used in the round
|
||||||
pub fn verify_partial_sig(
|
pub fn verify_partial_sig(
|
||||||
secp: &Secp256k1,
|
secp: &Secp256k1,
|
||||||
|
@ -277,9 +61,12 @@ pub fn verify_partial_sig(
|
||||||
pubkey: &PublicKey,
|
pubkey: &PublicKey,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
lock_height: u64,
|
lock_height: u64,
|
||||||
) -> bool {
|
) -> Result<(), Error> {
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap();
|
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?;
|
||||||
verify_single(secp, sig, &msg, Some(&pub_nonce_sum), pubkey, true)
|
if !verify_single(secp, sig, &msg, Some(&pub_nonce_sum), pubkey, true) {
|
||||||
|
return Err(Error::Signature("Signature validation error".to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just a simple sig, creates its own nonce, etc
|
/// Just a simple sig, creates its own nonce, etc
|
||||||
|
@ -300,11 +87,14 @@ pub fn verify_single_from_commit(
|
||||||
sig: &Signature,
|
sig: &Signature,
|
||||||
msg: &Message,
|
msg: &Message,
|
||||||
commit: &Commitment,
|
commit: &Commitment,
|
||||||
) -> bool {
|
) -> Result<(), Error> {
|
||||||
// Extract the pubkey, unfortunately we need this hack for now, (we just hope
|
// Extract the pubkey, unfortunately we need this hack for now, (we just hope
|
||||||
// one is valid)
|
// one is valid)
|
||||||
let pubkey = commit.to_pubkey(secp).unwrap();
|
let pubkey = commit.to_pubkey(secp)?;
|
||||||
aggsig::verify_single(secp, &sig, &msg, None, &pubkey, false)
|
if !verify_single(secp, sig, &msg, None, &pubkey, false) {
|
||||||
|
return Err(Error::Signature("Signature validation error".to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify a sig, with built message
|
/// Verify a sig, with built message
|
||||||
|
@ -314,12 +104,15 @@ pub fn verify_sig_build_msg(
|
||||||
pubkey: &PublicKey,
|
pubkey: &PublicKey,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
lock_height: u64,
|
lock_height: u64,
|
||||||
) -> bool {
|
) -> Result<(), Error> {
|
||||||
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height)).unwrap();
|
let msg = secp::Message::from_slice(&kernel_sig_msg(fee, lock_height))?;
|
||||||
verify_single(secp, sig, &msg, None, pubkey, true)
|
if !verify_single(secp, sig, &msg, None, pubkey, true) {
|
||||||
|
return Err(Error::Signature("Signature validation error".to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
//Verifies an aggsig signature
|
/// Verifies an aggsig signature
|
||||||
pub fn verify_single(
|
pub fn verify_single(
|
||||||
secp: &Secp256k1,
|
secp: &Secp256k1,
|
||||||
sig: &Signature,
|
sig: &Signature,
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
// Stub while figuring out wallet redesign
|
|
|
@ -27,12 +27,12 @@
|
||||||
|
|
||||||
use util::{kernel_sig_msg, secp};
|
use util::{kernel_sig_msg, secp};
|
||||||
|
|
||||||
use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel};
|
|
||||||
use core::core::hash::Hash;
|
use core::core::hash::Hash;
|
||||||
use core::core::pmmr::MerkleProof;
|
use core::core::pmmr::MerkleProof;
|
||||||
use libwallet::{aggsig, proof};
|
use core::core::{Input, Output, OutputFeatures, ProofMessageElements, Transaction, TxKernel};
|
||||||
use keychain;
|
use keychain;
|
||||||
use keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
|
use keychain::{BlindSum, BlindingFactor, Identifier, Keychain};
|
||||||
|
use libwallet::{aggsig, proof};
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
/// Context information available to transaction combinators.
|
/// Context information available to transaction combinators.
|
||||||
|
@ -167,7 +167,8 @@ pub fn with_offset(offset: BlindingFactor) -> Box<Append> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets an initial transaction to add to when building a new transaction.
|
/// Sets an initial transaction to add to when building a new transaction.
|
||||||
/// We currently only support building a tx with a single kernel with build::transaction()
|
/// We currently only support building a tx with a single kernel with
|
||||||
|
/// build::transaction()
|
||||||
pub fn initial_tx(mut tx: Transaction) -> Box<Append> {
|
pub fn initial_tx(mut tx: Transaction) -> Box<Append> {
|
||||||
assert_eq!(tx.kernels.len(), 1);
|
assert_eq!(tx.kernels.len(), 1);
|
||||||
let kern = tx.kernels.remove(0);
|
let kern = tx.kernels.remove(0);
|
||||||
|
|
|
@ -14,16 +14,34 @@
|
||||||
|
|
||||||
//! Wallet lib errors
|
//! Wallet lib errors
|
||||||
|
|
||||||
use util::secp;
|
use core::core::transaction;
|
||||||
use keychain::{self, extkey};
|
use keychain::{self, extkey};
|
||||||
|
use util::secp;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
#[derive(Fail, PartialEq, Clone, Debug)]
|
||||||
|
/// Libwallet error types
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// SECP error
|
||||||
|
#[fail(display = "Secp Error")]
|
||||||
Secp(secp::Error),
|
Secp(secp::Error),
|
||||||
|
/// Keychain error
|
||||||
|
#[fail(display = "Keychain Error")]
|
||||||
Keychain(keychain::Error),
|
Keychain(keychain::Error),
|
||||||
|
/// Extended key error
|
||||||
|
#[fail(display = "Extended Key Error")]
|
||||||
ExtendedKey(extkey::Error),
|
ExtendedKey(extkey::Error),
|
||||||
Transaction(String),
|
/// Transaction error
|
||||||
|
#[fail(display = "Transaction Error")]
|
||||||
|
Transaction(transaction::Error),
|
||||||
|
/// Signature error
|
||||||
|
#[fail(display = "Signature Error")]
|
||||||
|
Signature(String),
|
||||||
|
/// Rangeproof error
|
||||||
|
#[fail(display = "Rangeproof Error")]
|
||||||
RangeProof(String),
|
RangeProof(String),
|
||||||
|
/// Fee error
|
||||||
|
#[fail(display = "Fee Error")]
|
||||||
|
Fee(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<secp::Error> for Error {
|
impl From<secp::Error> for Error {
|
||||||
|
@ -43,18 +61,9 @@ impl From<keychain::Error> for Error {
|
||||||
Error::Keychain(e)
|
Error::Keychain(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*impl error::Error for Error {
|
|
||||||
fn description(&self) -> &str {
|
impl From<transaction::Error> for Error {
|
||||||
match *self {
|
fn from(e: transaction::Error) -> Error {
|
||||||
_ => "some kind of wallet lib error",
|
Error::Transaction(e)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
_ => write!(f, "some kind of wallet lib error"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
#![deny(unused_mut)]
|
#![deny(unused_mut)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod aggsig;
|
pub mod aggsig;
|
||||||
pub mod blind;
|
pub mod build;
|
||||||
|
pub mod error;
|
||||||
pub mod proof;
|
pub mod proof;
|
||||||
pub mod reward;
|
pub mod reward;
|
||||||
pub mod build;
|
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
|
@ -14,16 +14,16 @@
|
||||||
|
|
||||||
//! Rangeproof library functions
|
//! Rangeproof library functions
|
||||||
|
|
||||||
|
use blake2;
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
|
|
||||||
use util::secp::key::SecretKey;
|
|
||||||
use util::secp::{self, Secp256k1};
|
|
||||||
use util::logger::LOGGER;
|
|
||||||
use keychain::extkey::Identifier;
|
use keychain::extkey::Identifier;
|
||||||
use libwallet::error::Error;
|
use libwallet::error::Error;
|
||||||
use blake2;
|
use util::logger::LOGGER;
|
||||||
|
use util::secp::key::SecretKey;
|
||||||
|
use util::secp::pedersen::{Commitment, ProofInfo, ProofMessage, RangeProof};
|
||||||
|
use util::secp::{self, Secp256k1};
|
||||||
|
|
||||||
pub fn create_nonce(k: &Keychain, commit: &Commitment) -> SecretKey {
|
fn create_nonce(k: &Keychain, commit: &Commitment) -> Result<SecretKey, Error> {
|
||||||
// hash(commit|masterkey) as nonce
|
// hash(commit|masterkey) as nonce
|
||||||
let root_key = k.root_key_id().to_bytes();
|
let root_key = k.root_key_id().to_bytes();
|
||||||
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key);
|
let res = blake2::blake2b::blake2b(32, &commit.0, &root_key);
|
||||||
|
@ -32,7 +32,12 @@ pub fn create_nonce(k: &Keychain, commit: &Commitment) -> SecretKey {
|
||||||
for i in 0..res.len() {
|
for i in 0..res.len() {
|
||||||
ret_val[i] = res[i];
|
ret_val[i] = res[i];
|
||||||
}
|
}
|
||||||
SecretKey::from_slice(k.secp(), &ret_val).unwrap()
|
match SecretKey::from_slice(k.secp(), &ret_val) {
|
||||||
|
Ok(sk) => Ok(sk),
|
||||||
|
Err(e) => Err(Error::RangeProof(
|
||||||
|
format!("Unable to create nonce: {:?}", e).to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// So we want this to take an opaque structure that can be called
|
/// So we want this to take an opaque structure that can be called
|
||||||
|
@ -48,7 +53,7 @@ pub fn create(
|
||||||
) -> Result<RangeProof, Error> {
|
) -> Result<RangeProof, Error> {
|
||||||
let commit = k.commit(amount, key_id)?;
|
let commit = k.commit(amount, key_id)?;
|
||||||
let skey = k.derived_key(key_id)?;
|
let skey = k.derived_key(key_id)?;
|
||||||
let nonce = create_nonce(k, &commit);
|
let nonce = create_nonce(k, &commit)?;
|
||||||
if msg.len() == 0 {
|
if msg.len() == 0 {
|
||||||
return Ok(k.secp().bullet_proof(amount, skey, nonce, extra_data, None));
|
return Ok(k.secp().bullet_proof(amount, skey, nonce, extra_data, None));
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,6 +68,7 @@ pub fn create(
|
||||||
.bullet_proof(amount, skey, nonce, extra_data, Some(msg)));
|
.bullet_proof(amount, skey, nonce, extra_data, Some(msg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify a proof
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
secp: &Secp256k1,
|
secp: &Secp256k1,
|
||||||
commit: Commitment,
|
commit: Commitment,
|
||||||
|
@ -76,6 +82,7 @@ pub fn verify(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rewind a rangeproof to retrieve the amount
|
||||||
pub fn rewind(
|
pub fn rewind(
|
||||||
k: &Keychain,
|
k: &Keychain,
|
||||||
key_id: &Identifier,
|
key_id: &Identifier,
|
||||||
|
@ -84,7 +91,7 @@ pub fn rewind(
|
||||||
proof: RangeProof,
|
proof: RangeProof,
|
||||||
) -> Result<ProofInfo, Error> {
|
) -> Result<ProofInfo, Error> {
|
||||||
let skey = k.derived_key(key_id)?;
|
let skey = k.derived_key(key_id)?;
|
||||||
let nonce = create_nonce(k, &commit);
|
let nonce = create_nonce(k, &commit)?;
|
||||||
let proof_message = k.secp()
|
let proof_message = k.secp()
|
||||||
.unwind_bullet_proof(commit, skey, nonce, extra_data, proof);
|
.unwind_bullet_proof(commit, skey, nonce, extra_data, proof);
|
||||||
let proof_info = match proof_message {
|
let proof_info = match proof_message {
|
||||||
|
|
|
@ -12,16 +12,15 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
/// Builds the blinded output and related signature proof for the block
|
//! Builds the blinded output and related signature proof for the block
|
||||||
/// reward.
|
//! reward.
|
||||||
|
|
||||||
use keychain;
|
use keychain;
|
||||||
|
|
||||||
use core::core::{Output, OutputFeatures, ProofMessageElements, TxKernel};
|
|
||||||
use core::consensus::reward;
|
use core::consensus::reward;
|
||||||
use libwallet::{aggsig, proof};
|
|
||||||
use libwallet::error::Error;
|
|
||||||
use core::core::KernelFeatures;
|
use core::core::KernelFeatures;
|
||||||
|
use core::core::{Output, OutputFeatures, ProofMessageElements, TxKernel};
|
||||||
|
use libwallet::error::Error;
|
||||||
|
use libwallet::{aggsig, proof};
|
||||||
use util::{kernel_sig_msg, secp, static_secp_instance, LOGGER};
|
use util::{kernel_sig_msg, secp, static_secp_instance, LOGGER};
|
||||||
|
|
||||||
/// output a reward output
|
/// output a reward output
|
||||||
|
|
|
@ -17,17 +17,17 @@
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use core::consensus;
|
||||||
use core::core::{amount_to_hr_string, Committed, Transaction};
|
use core::core::{amount_to_hr_string, Committed, Transaction};
|
||||||
use keychain::{BlindSum, BlindingFactor, Keychain};
|
use keychain::{BlindSum, BlindingFactor, Keychain};
|
||||||
|
use libwallet::error::Error;
|
||||||
use libwallet::{aggsig, build};
|
use libwallet::{aggsig, build};
|
||||||
//TODO: Remove these from here, replace with libwallet error
|
|
||||||
use types::{tx_fee, Error, ErrorKind};
|
|
||||||
|
|
||||||
use util::secp::Signature;
|
use util::secp::Signature;
|
||||||
use util::secp::key::{PublicKey, SecretKey};
|
use util::secp::key::{PublicKey, SecretKey};
|
||||||
use util::{secp, LOGGER};
|
use util::{secp, LOGGER};
|
||||||
|
|
||||||
use failure::ResultExt;
|
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
|
||||||
|
|
||||||
/// Public data for each participant in the slate
|
/// Public data for each participant in the slate
|
||||||
|
|
||||||
|
@ -114,8 +114,7 @@ impl Slate {
|
||||||
if self.tx.kernels.len() != 0 {
|
if self.tx.kernels.len() != 0 {
|
||||||
elems.insert(0, build::initial_tx(self.tx.clone()));
|
elems.insert(0, build::initial_tx(self.tx.clone()));
|
||||||
}
|
}
|
||||||
let (tx, blind) =
|
let (tx, blind) = build::partial_transaction(elems, &keychain)?;
|
||||||
build::partial_transaction(elems, &keychain).context(ErrorKind::Keychain)?;
|
|
||||||
self.tx = tx;
|
self.tx = tx;
|
||||||
Ok(blind)
|
Ok(blind)
|
||||||
}
|
}
|
||||||
|
@ -125,14 +124,15 @@ impl Slate {
|
||||||
pub fn fill_round_1(
|
pub fn fill_round_1(
|
||||||
&mut self,
|
&mut self,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
sec_key: &mut SecretKey,
|
||||||
|
sec_nonce: &SecretKey,
|
||||||
participant_id: usize,
|
participant_id: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Whoever does this first generates the offset
|
// Whoever does this first generates the offset
|
||||||
if self.tx.offset == BlindingFactor::zero() {
|
if self.tx.offset == BlindingFactor::zero() {
|
||||||
self.generate_offset(keychain, context_manager)?;
|
self.generate_offset(keychain, sec_key)?;
|
||||||
}
|
}
|
||||||
self.add_participant_info(keychain, context_manager, participant_id, None)?;
|
self.add_participant_info(keychain, &sec_key, &sec_nonce, participant_id, None)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,20 +140,20 @@ impl Slate {
|
||||||
pub fn fill_round_2(
|
pub fn fill_round_2(
|
||||||
&mut self,
|
&mut self,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
sec_key: &SecretKey,
|
||||||
|
sec_nonce: &SecretKey,
|
||||||
participant_id: usize,
|
participant_id: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.check_fees()?;
|
self.check_fees()?;
|
||||||
self.verify_part_sigs(keychain.secp())?;
|
self.verify_part_sigs(keychain.secp())?;
|
||||||
let context = context_manager.get_context(&self.id);
|
let sig_part = aggsig::calculate_partial_sig(
|
||||||
let sig_part = context
|
keychain.secp(),
|
||||||
.calculate_partial_sig_with_nonce_sum(
|
sec_key,
|
||||||
keychain.secp(),
|
sec_nonce,
|
||||||
&self.pub_nonce_sum(keychain.secp()),
|
&self.pub_nonce_sum(keychain.secp())?,
|
||||||
self.fee,
|
self.fee,
|
||||||
self.lock_height,
|
self.lock_height,
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
|
||||||
self.participant_data[participant_id].part_sig = Some(sig_part);
|
self.participant_data[participant_id].part_sig = Some(sig_part);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -167,21 +167,27 @@ impl Slate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the sum of public nonces
|
/// Return the sum of public nonces
|
||||||
fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> PublicKey {
|
fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
|
||||||
let pub_nonces = self.participant_data
|
let pub_nonces = self.participant_data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| &p.public_nonce)
|
.map(|p| &p.public_nonce)
|
||||||
.collect();
|
.collect();
|
||||||
PublicKey::from_combination(secp, pub_nonces).unwrap()
|
match PublicKey::from_combination(secp, pub_nonces) {
|
||||||
|
Ok(k) => Ok(k),
|
||||||
|
Err(e) => Err(Error::Secp(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the sum of public blinding factors
|
/// Return the sum of public blinding factors
|
||||||
fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> PublicKey {
|
fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
|
||||||
let pub_blinds = self.participant_data
|
let pub_blinds = self.participant_data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| &p.public_blind_excess)
|
.map(|p| &p.public_blind_excess)
|
||||||
.collect();
|
.collect();
|
||||||
PublicKey::from_combination(secp, pub_blinds).unwrap()
|
match PublicKey::from_combination(secp, pub_blinds) {
|
||||||
|
Ok(k) => Ok(k),
|
||||||
|
Err(e) => Err(Error::Secp(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return vector of all partial sigs
|
/// Return vector of all partial sigs
|
||||||
|
@ -199,14 +205,14 @@ impl Slate {
|
||||||
fn add_participant_info(
|
fn add_participant_info(
|
||||||
&mut self,
|
&mut self,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &aggsig::ContextManager,
|
sec_key: &SecretKey,
|
||||||
|
sec_nonce: &SecretKey,
|
||||||
id: usize,
|
id: usize,
|
||||||
part_sig: Option<Signature>,
|
part_sig: Option<Signature>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let context = context_manager.get_context(&self.id);
|
|
||||||
|
|
||||||
// Add our public key and nonce to the slate
|
// Add our public key and nonce to the slate
|
||||||
let (pub_key, pub_nonce) = context.get_public_keys(keychain.secp());
|
let pub_key = PublicKey::from_secret_key(keychain.secp(), &sec_key)?;
|
||||||
|
let pub_nonce = PublicKey::from_secret_key(keychain.secp(), &sec_nonce)?;
|
||||||
self.participant_data.push(ParticipantData {
|
self.participant_data.push(ParticipantData {
|
||||||
id: id as u64,
|
id: id as u64,
|
||||||
public_blind_excess: pub_key,
|
public_blind_excess: pub_key,
|
||||||
|
@ -219,27 +225,22 @@ impl Slate {
|
||||||
|
|
||||||
/// Somebody involved needs to generate an offset with their private key
|
/// Somebody involved needs to generate an offset with their private key
|
||||||
/// For now, we'll have the transaction initiator be responsible for it
|
/// For now, we'll have the transaction initiator be responsible for it
|
||||||
/// Return offset private key
|
/// Return offset private key for the participant to use later in the
|
||||||
|
/// transaction
|
||||||
fn generate_offset(
|
fn generate_offset(
|
||||||
&mut self,
|
&mut self,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
sec_key: &mut SecretKey,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Generate a random kernel offset here
|
// Generate a random kernel offset here
|
||||||
// and subtract it from the blind_sum so we create
|
// and subtract it from the blind_sum so we create
|
||||||
// the aggsig context with the "split" key
|
// the aggsig context with the "split" key
|
||||||
let mut context = context_manager.get_context(&self.id);
|
|
||||||
self.tx.offset =
|
self.tx.offset =
|
||||||
BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut thread_rng()));
|
BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut thread_rng()));
|
||||||
let blind_offset = keychain
|
let blind_offset = keychain.blind_sum(&BlindSum::new()
|
||||||
.blind_sum(&BlindSum::new()
|
.add_blinding_factor(BlindingFactor::from_secret_key(sec_key.clone()))
|
||||||
.add_blinding_factor(BlindingFactor::from_secret_key(context.sec_key))
|
.sub_blinding_factor(self.tx.offset))?;
|
||||||
.sub_blinding_factor(self.tx.offset))
|
*sec_key = blind_offset.secret_key(&keychain.secp())?;
|
||||||
.unwrap();
|
|
||||||
context.sec_key = blind_offset
|
|
||||||
.secret_key(&keychain.secp())
|
|
||||||
.context(ErrorKind::Keychain)?;
|
|
||||||
context_manager.save_context(context);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,23 +256,19 @@ impl Slate {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
if fee > self.tx.fee() {
|
if fee > self.tx.fee() {
|
||||||
return Err(ErrorKind::FeeDispute {
|
return Err(Error::Fee(
|
||||||
sender_fee: self.tx.fee(),
|
format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(),
|
||||||
recipient_fee: fee,
|
));
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if fee > self.amount + self.fee {
|
if fee > self.amount + self.fee {
|
||||||
info!(
|
let reason = format!(
|
||||||
LOGGER,
|
|
||||||
"Rejected the transfer because transaction fee ({}) exceeds received amount ({}).",
|
"Rejected the transfer because transaction fee ({}) exceeds received amount ({}).",
|
||||||
amount_to_hr_string(fee),
|
amount_to_hr_string(fee),
|
||||||
amount_to_hr_string(self.amount + self.fee)
|
amount_to_hr_string(self.amount + self.fee)
|
||||||
);
|
);
|
||||||
return Err(ErrorKind::FeeExceedsAmount {
|
info!(LOGGER, "{}", reason);
|
||||||
sender_amount: self.amount + self.fee,
|
return Err(Error::Fee(reason.to_string()));
|
||||||
recipient_fee: fee,
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -282,18 +279,14 @@ impl Slate {
|
||||||
// collect public nonces
|
// collect public nonces
|
||||||
for p in self.participant_data.iter() {
|
for p in self.participant_data.iter() {
|
||||||
if p.is_complete() {
|
if p.is_complete() {
|
||||||
if aggsig::verify_partial_sig(
|
aggsig::verify_partial_sig(
|
||||||
secp,
|
secp,
|
||||||
p.part_sig.as_ref().unwrap(),
|
p.part_sig.as_ref().unwrap(),
|
||||||
&self.pub_nonce_sum(secp),
|
&self.pub_nonce_sum(secp)?,
|
||||||
&p.public_blind_excess,
|
&p.public_blind_excess,
|
||||||
self.fee,
|
self.fee,
|
||||||
self.lock_height,
|
self.lock_height,
|
||||||
) == false
|
)?;
|
||||||
{
|
|
||||||
error!(LOGGER, "Partial Sig invalid.");
|
|
||||||
return Err(ErrorKind::Signature("Partial Sig invalid."))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -320,27 +313,21 @@ impl Slate {
|
||||||
self.verify_part_sigs(keychain.secp())?;
|
self.verify_part_sigs(keychain.secp())?;
|
||||||
|
|
||||||
let part_sigs = self.part_sigs();
|
let part_sigs = self.part_sigs();
|
||||||
let pub_nonce_sum = self.pub_nonce_sum(keychain.secp());
|
let pub_nonce_sum = self.pub_nonce_sum(keychain.secp())?;
|
||||||
let final_pubkey = self.pub_blind_sum(keychain.secp());
|
let final_pubkey = self.pub_blind_sum(keychain.secp())?;
|
||||||
// get the final signature
|
// get the final signature
|
||||||
let final_sig =
|
let final_sig = aggsig::add_signatures(&keychain.secp(), part_sigs, &pub_nonce_sum)?;
|
||||||
aggsig::add_signatures(&keychain.secp(), part_sigs, &pub_nonce_sum).unwrap();
|
|
||||||
|
|
||||||
// Calculate the final public key (for our own sanity check)
|
// Calculate the final public key (for our own sanity check)
|
||||||
|
|
||||||
// Check our final sig verifies
|
// Check our final sig verifies
|
||||||
let res = aggsig::verify_sig_build_msg(
|
aggsig::verify_sig_build_msg(
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
&final_sig,
|
&final_sig,
|
||||||
&final_pubkey,
|
&final_pubkey,
|
||||||
self.fee,
|
self.fee,
|
||||||
self.lock_height,
|
self.lock_height,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
if !res {
|
|
||||||
error!(LOGGER, "Final aggregated signature invalid.");
|
|
||||||
return Err(ErrorKind::Signature("Final aggregated signature invalid."))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(final_sig)
|
Ok(final_sig)
|
||||||
}
|
}
|
||||||
|
@ -361,24 +348,20 @@ impl Slate {
|
||||||
let final_excess = {
|
let final_excess = {
|
||||||
// TODO - do we need to verify rangeproofs here?
|
// TODO - do we need to verify rangeproofs here?
|
||||||
for x in &final_tx.outputs {
|
for x in &final_tx.outputs {
|
||||||
x.verify_proof().context(ErrorKind::Transaction)?;
|
x.verify_proof()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sum the input/output commitments on the final tx
|
// sum the input/output commitments on the final tx
|
||||||
let overage = final_tx.fee() as i64;
|
let overage = final_tx.fee() as i64;
|
||||||
let tx_excess = final_tx
|
let tx_excess = final_tx.sum_commitments(overage, None)?;
|
||||||
.sum_commitments(overage, None)
|
|
||||||
.context(ErrorKind::Transaction)?;
|
|
||||||
|
|
||||||
// subtract the kernel_excess (built from kernel_offset)
|
// subtract the kernel_excess (built from kernel_offset)
|
||||||
let offset_excess = keychain
|
let offset_excess = keychain
|
||||||
.secp()
|
.secp()
|
||||||
.commit(0, kernel_offset.secret_key(&keychain.secp()).unwrap())
|
.commit(0, kernel_offset.secret_key(&keychain.secp())?)?;
|
||||||
.unwrap();
|
|
||||||
keychain
|
keychain
|
||||||
.secp()
|
.secp()
|
||||||
.commit_sum(vec![tx_excess], vec![offset_excess])
|
.commit_sum(vec![tx_excess], vec![offset_excess])?
|
||||||
.context(ErrorKind::Transaction)?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the tx kernel to reflect the offset excess and sig
|
// update the tx kernel to reflect the offset excess and sig
|
||||||
|
@ -388,14 +371,22 @@ impl Slate {
|
||||||
|
|
||||||
// confirm the kernel verifies successfully before proceeding
|
// confirm the kernel verifies successfully before proceeding
|
||||||
debug!(LOGGER, "Validating final transaction");
|
debug!(LOGGER, "Validating final transaction");
|
||||||
final_tx.kernels[0]
|
final_tx.kernels[0].verify()?;
|
||||||
.verify()
|
|
||||||
.context(ErrorKind::Transaction)?;
|
|
||||||
|
|
||||||
// confirm the overall transaction is valid (including the updated kernel)
|
// confirm the overall transaction is valid (including the updated kernel)
|
||||||
let _ = final_tx.validate().context(ErrorKind::Transaction)?;
|
let _ = final_tx.validate()?;
|
||||||
|
|
||||||
self.tx = final_tx;
|
self.tx = final_tx;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transaction fee calculation
|
||||||
|
pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option<u64>) -> u64 {
|
||||||
|
let use_base_fee = match base_fee {
|
||||||
|
Some(bf) => bf,
|
||||||
|
None => DEFAULT_BASE_FEE,
|
||||||
|
};
|
||||||
|
|
||||||
|
(Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee
|
||||||
|
}
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use checker;
|
use checker;
|
||||||
use keychain::Keychain;
|
|
||||||
use core::core;
|
use core::core;
|
||||||
use types::{OutputStatus, WalletConfig, WalletData};
|
use keychain::Keychain;
|
||||||
use prettytable;
|
use prettytable;
|
||||||
use term;
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use term;
|
||||||
|
use types::{OutputStatus, WalletConfig, WalletData};
|
||||||
|
|
||||||
pub fn show_outputs(config: &WalletConfig, keychain: &Keychain, show_spent: bool) {
|
pub fn show_outputs(config: &WalletConfig, keychain: &Keychain, show_spent: bool) {
|
||||||
let root_key_id = keychain.root_key_id();
|
let root_key_id = keychain.root_key_id();
|
||||||
|
|
|
@ -21,16 +21,16 @@ use iron::Handler;
|
||||||
use iron::prelude::*;
|
use iron::prelude::*;
|
||||||
use iron::status;
|
use iron::status;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use api;
|
use api;
|
||||||
use core::consensus::reward;
|
use core::consensus::reward;
|
||||||
use core::core::{Output, TxKernel};
|
use core::core::{Output, TxKernel};
|
||||||
use core::global;
|
use core::global;
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
|
use failure::ResultExt;
|
||||||
use grinwallet::{keys, selection};
|
use grinwallet::{keys, selection};
|
||||||
use keychain::Keychain;
|
use keychain::Keychain;
|
||||||
use libwallet::{aggsig, reward, transaction};
|
use libwallet::{reward, transaction};
|
||||||
use types::*;
|
use types::*;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
||||||
|
@ -40,28 +40,24 @@ pub struct TxWrapper {
|
||||||
pub tx_hex: String,
|
pub tx_hex: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
/// Static reference to aggsig context (temporary while wallet is being refactored)
|
|
||||||
pub static ref AGGSIG_CONTEXT_MANAGER:Arc<RwLock<aggsig::ContextManager>>
|
|
||||||
= Arc::new(RwLock::new(aggsig::ContextManager::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_send(
|
fn handle_send(
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
context_manager: &mut aggsig::ContextManager,
|
|
||||||
slate: &mut transaction::Slate,
|
slate: &mut transaction::Slate,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// create an output using the amount in the slate
|
// create an output using the amount in the slate
|
||||||
let (_, receiver_create_fn) =
|
let (_, mut context, receiver_create_fn) =
|
||||||
selection::build_recipient_output_with_slate(config, keychain, context_manager, slate)
|
selection::build_recipient_output_with_slate(config, keychain, slate).unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// fill public keys
|
// fill public keys
|
||||||
let _ = slate.fill_round_1(&keychain, context_manager, 1)?;
|
let _ = slate
|
||||||
|
.fill_round_1(&keychain, &mut context.sec_key, &context.sec_nonce, 1)
|
||||||
|
.context(ErrorKind::LibWalletError)?;
|
||||||
|
|
||||||
// perform partial sig
|
// perform partial sig
|
||||||
let _ = slate.fill_round_2(&keychain, context_manager, 1)?;
|
let _ = slate
|
||||||
|
.fill_round_2(&keychain, &context.sec_key, &context.sec_nonce, 1)
|
||||||
|
.context(ErrorKind::LibWalletError)?;
|
||||||
|
|
||||||
// Save output in wallet
|
// Save output in wallet
|
||||||
let _ = receiver_create_fn();
|
let _ = receiver_create_fn();
|
||||||
|
@ -82,8 +78,7 @@ impl Handler for WalletReceiver {
|
||||||
let struct_body = req.get::<bodyparser::Struct<transaction::Slate>>();
|
let struct_body = req.get::<bodyparser::Struct<transaction::Slate>>();
|
||||||
|
|
||||||
if let Ok(Some(mut slate)) = struct_body {
|
if let Ok(Some(mut slate)) = struct_body {
|
||||||
let mut acm = AGGSIG_CONTEXT_MANAGER.write().unwrap();
|
let _ = handle_send(&self.config, &self.keychain, &mut slate)
|
||||||
let _ = handle_send(&self.config, &self.keychain, &mut acm, &mut slate)
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!(
|
error!(
|
||||||
LOGGER,
|
LOGGER,
|
||||||
|
|
|
@ -11,18 +11,18 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
use api;
|
||||||
|
use byteorder::{BigEndian, ByteOrder};
|
||||||
|
use core::core::transaction::ProofMessageElements;
|
||||||
|
use core::global;
|
||||||
use failure::{Fail, ResultExt};
|
use failure::{Fail, ResultExt};
|
||||||
use keychain::{Identifier, Keychain};
|
use keychain::{Identifier, Keychain};
|
||||||
|
use libwallet::proof;
|
||||||
|
use types::{Error, ErrorKind, MerkleProofWrapper, OutputData, OutputStatus, WalletConfig,
|
||||||
|
WalletData};
|
||||||
use util;
|
use util;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use api;
|
|
||||||
use core::global;
|
|
||||||
use core::core::transaction::ProofMessageElements;
|
|
||||||
use types::{Error, ErrorKind, MerkleProofWrapper, OutputData, OutputStatus, WalletConfig,
|
|
||||||
WalletData};
|
|
||||||
use byteorder::{BigEndian, ByteOrder};
|
|
||||||
use libwallet::proof;
|
|
||||||
|
|
||||||
pub fn get_chain_height(config: &WalletConfig) -> Result<u64, Error> {
|
pub fn get_chain_height(config: &WalletConfig) -> Result<u64, Error> {
|
||||||
let url = format!("{}/v1/chain", config.check_node_api_http_addr);
|
let url = format!("{}/v1/chain", config.check_node_api_http_addr);
|
||||||
|
@ -105,8 +105,17 @@ fn find_outputs_with_key(
|
||||||
keychain: &Keychain,
|
keychain: &Keychain,
|
||||||
outputs: Vec<api::OutputPrintable>,
|
outputs: Vec<api::OutputPrintable>,
|
||||||
found_key_index: &mut Vec<u32>,
|
found_key_index: &mut Vec<u32>,
|
||||||
) -> Vec<
|
) -> Vec<(
|
||||||
(
|
pedersen::Commitment,
|
||||||
|
Identifier,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
u64,
|
||||||
|
bool,
|
||||||
|
Option<MerkleProofWrapper>,
|
||||||
|
)> {
|
||||||
|
let mut wallet_outputs: Vec<(
|
||||||
pedersen::Commitment,
|
pedersen::Commitment,
|
||||||
Identifier,
|
Identifier,
|
||||||
u32,
|
u32,
|
||||||
|
@ -115,20 +124,7 @@ fn find_outputs_with_key(
|
||||||
u64,
|
u64,
|
||||||
bool,
|
bool,
|
||||||
Option<MerkleProofWrapper>,
|
Option<MerkleProofWrapper>,
|
||||||
),
|
)> = Vec::new();
|
||||||
> {
|
|
||||||
let mut wallet_outputs: Vec<
|
|
||||||
(
|
|
||||||
pedersen::Commitment,
|
|
||||||
Identifier,
|
|
||||||
u32,
|
|
||||||
u64,
|
|
||||||
u64,
|
|
||||||
u64,
|
|
||||||
bool,
|
|
||||||
Option<MerkleProofWrapper>,
|
|
||||||
),
|
|
||||||
> = Vec::new();
|
|
||||||
|
|
||||||
let max_derivations = 1_000_000;
|
let max_derivations = 1_000_000;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use core::ser;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use grinwallet::selection;
|
use grinwallet::selection;
|
||||||
use keychain::{Identifier, Keychain};
|
use keychain::{Identifier, Keychain};
|
||||||
use libwallet::{aggsig, build};
|
use libwallet::{build, transaction};
|
||||||
use receiver::TxWrapper;
|
use receiver::TxWrapper;
|
||||||
use types::*;
|
use types::*;
|
||||||
use util;
|
use util;
|
||||||
|
@ -50,9 +50,6 @@ pub fn issue_send_tx(
|
||||||
|
|
||||||
checker::refresh_outputs(config, keychain)?;
|
checker::refresh_outputs(config, keychain)?;
|
||||||
|
|
||||||
// Create a new aggsig context
|
|
||||||
let mut context_manager = aggsig::ContextManager::new();
|
|
||||||
|
|
||||||
// Get lock height
|
// Get lock height
|
||||||
let chain_tip = checker::get_tip_from_node(config)?;
|
let chain_tip = checker::get_tip_from_node(config)?;
|
||||||
let current_height = chain_tip.height;
|
let current_height = chain_tip.height;
|
||||||
|
@ -61,17 +58,16 @@ pub fn issue_send_tx(
|
||||||
|
|
||||||
let lock_height = current_height;
|
let lock_height = current_height;
|
||||||
|
|
||||||
// Sender selects outputs into a new slate and save our corresponding IDs in
|
// Sender selects outputs into a new slate and save our corresponding keyss in
|
||||||
// their transaction context. The secret key in our transaction context will be
|
// a transaction context. The secret key in our transaction context will be
|
||||||
// randomly selected. This returns the public slate, and a closure that locks
|
// randomly selected. This returns the public slate, and a closure that locks
|
||||||
// our inputs and outputs once we're convinced the transaction exchange went
|
// our inputs and outputs once we're convinced the transaction exchange went
|
||||||
// according to plan
|
// according to plan
|
||||||
// This function is just a big helper to do all of that, in theory
|
// This function is just a big helper to do all of that, in theory
|
||||||
// this process can be split up in any way
|
// this process can be split up in any way
|
||||||
let (mut slate, sender_lock_fn) = selection::build_send_tx_slate(
|
let (mut slate, mut context, sender_lock_fn) = selection::build_send_tx_slate(
|
||||||
config,
|
config,
|
||||||
keychain,
|
keychain,
|
||||||
&mut context_manager,
|
|
||||||
2,
|
2,
|
||||||
amount,
|
amount,
|
||||||
current_height,
|
current_height,
|
||||||
|
@ -85,7 +81,7 @@ pub fn issue_send_tx(
|
||||||
// the offset in the slate's transaction kernel, and adds our public key
|
// the offset in the slate's transaction kernel, and adds our public key
|
||||||
// information to the slate
|
// information to the slate
|
||||||
let _ = slate
|
let _ = slate
|
||||||
.fill_round_1(keychain, &mut context_manager, 0)
|
.fill_round_1(keychain, &mut context.sec_key, &context.sec_nonce, 0)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let url = format!("{}/v1/receive/transaction", &dest);
|
let url = format!("{}/v1/receive/transaction", &dest);
|
||||||
|
@ -112,10 +108,12 @@ pub fn issue_send_tx(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = slate.fill_round_2(keychain, &mut context_manager, 0)?;
|
let _ = slate
|
||||||
|
.fill_round_2(keychain, &context.sec_key, &context.sec_nonce, 0)
|
||||||
|
.context(ErrorKind::LibWalletError)?;
|
||||||
|
|
||||||
// Final transaction can be built by anyone at this stage
|
// Final transaction can be built by anyone at this stage
|
||||||
slate.finalize(keychain)?;
|
slate.finalize(keychain).context(ErrorKind::LibWalletError)?;
|
||||||
|
|
||||||
// So let's post it
|
// So let's post it
|
||||||
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
let tx_hex = util::to_hex(ser::ser_vec(&slate.tx).unwrap());
|
||||||
|
@ -130,7 +128,7 @@ pub fn issue_send_tx(
|
||||||
}
|
}
|
||||||
api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).context(ErrorKind::Node)?;
|
api::client::post(url.as_str(), &TxWrapper { tx_hex: tx_hex }).context(ErrorKind::Node)?;
|
||||||
|
|
||||||
// All good so, lock our outputs
|
// All good so, lock our inputs
|
||||||
sender_lock_fn()?;
|
sender_lock_fn()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -165,8 +163,9 @@ pub fn issue_burn_tx(
|
||||||
|
|
||||||
debug!(LOGGER, "selected some coins - {}", coins.len());
|
debug!(LOGGER, "selected some coins - {}", coins.len());
|
||||||
|
|
||||||
let fee = tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None);
|
let fee = transaction::tx_fee(coins.len(), 2, selection::coins_proof_count(&coins), None);
|
||||||
let (mut parts, _) = selection::inputs_and_change(&coins, config, keychain, amount, fee)?;
|
let (mut parts, _) =
|
||||||
|
selection::inputs_and_change(&coins, config, keychain, current_height, amount, fee)?;
|
||||||
|
|
||||||
// add burn output and fees
|
// add burn output and fees
|
||||||
parts.push(build::output(amount - fee, Identifier::zero()));
|
parts.push(build::output(amount - fee, Identifier::zero()));
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use api::ApiServer;
|
use api::ApiServer;
|
||||||
use keychain::Keychain;
|
|
||||||
use handlers::CoinbaseHandler;
|
use handlers::CoinbaseHandler;
|
||||||
|
use keychain::Keychain;
|
||||||
use receiver::WalletReceiver;
|
use receiver::WalletReceiver;
|
||||||
use types::WalletConfig;
|
use types::WalletConfig;
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
|
|
|
@ -32,8 +32,6 @@ use tokio_retry::strategy::FibonacciBackoff;
|
||||||
|
|
||||||
use failure::{Backtrace, Context, Fail, ResultExt};
|
use failure::{Backtrace, Context, Fail, ResultExt};
|
||||||
|
|
||||||
use core::consensus;
|
|
||||||
use core::core::Transaction;
|
|
||||||
use core::core::hash::Hash;
|
use core::core::hash::Hash;
|
||||||
use core::core::pmmr::MerkleProof;
|
use core::core::pmmr::MerkleProof;
|
||||||
use keychain;
|
use keychain;
|
||||||
|
@ -45,18 +43,6 @@ const BCK_FILE: &'static str = "wallet.bck";
|
||||||
const LOCK_FILE: &'static str = "wallet.lock";
|
const LOCK_FILE: &'static str = "wallet.lock";
|
||||||
const SEED_FILE: &'static str = "wallet.seed";
|
const SEED_FILE: &'static str = "wallet.seed";
|
||||||
|
|
||||||
const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN;
|
|
||||||
|
|
||||||
/// Transaction fee calculation
|
|
||||||
pub fn tx_fee(input_len: usize, output_len: usize, proof_len: usize, base_fee: Option<u64>) -> u64 {
|
|
||||||
let use_base_fee = match base_fee {
|
|
||||||
Some(bf) => bf,
|
|
||||||
None => DEFAULT_BASE_FEE,
|
|
||||||
};
|
|
||||||
|
|
||||||
(Transaction::weight(input_len, output_len, proof_len) as u64) * use_base_fee
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
inner: Context<ErrorKind>,
|
inner: Context<ErrorKind>,
|
||||||
|
@ -87,6 +73,9 @@ pub enum ErrorKind {
|
||||||
#[fail(display = "Secp error")]
|
#[fail(display = "Secp error")]
|
||||||
Secp,
|
Secp,
|
||||||
|
|
||||||
|
#[fail(display = "LibWallet error")]
|
||||||
|
LibWalletError,
|
||||||
|
|
||||||
#[fail(display = "Wallet data error: {}", _0)]
|
#[fail(display = "Wallet data error: {}", _0)]
|
||||||
WalletData(&'static str),
|
WalletData(&'static str),
|
||||||
|
|
||||||
|
|
|
@ -42,16 +42,21 @@ pub fn refresh_output_state_local(
|
||||||
chain: &chain::Chain,
|
chain: &chain::Chain,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let wallet_outputs = checker::map_wallet_outputs(config, keychain)?;
|
let wallet_outputs = checker::map_wallet_outputs(config, keychain)?;
|
||||||
let chain_outputs: Vec<api::Output> = wallet_outputs
|
let chain_outputs: Vec<Option<api::Output>> = wallet_outputs
|
||||||
.keys()
|
.keys()
|
||||||
.map(|k| match get_output_local(chain, &k) {
|
.map(|k| match get_output_local(chain, &k) {
|
||||||
Err(e) => panic!(e),
|
Err(_) => None,
|
||||||
Ok(k) => k,
|
Ok(k) => Some(k),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut api_outputs: HashMap<pedersen::Commitment, api::Output> = HashMap::new();
|
let mut api_outputs: HashMap<pedersen::Commitment, api::Output> = HashMap::new();
|
||||||
for out in chain_outputs {
|
for out in chain_outputs {
|
||||||
api_outputs.insert(out.commit.commit(), out);
|
match out {
|
||||||
|
Some(o) => {
|
||||||
|
api_outputs.insert(o.commit.commit(), o);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
checker::apply_api_outputs(config, &wallet_outputs, &api_outputs)?;
|
checker::apply_api_outputs(config, &wallet_outputs, &api_outputs)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -24,7 +24,7 @@ use keychain::{BlindSum, BlindingFactor, Keychain};
|
||||||
use util::secp::key::{PublicKey, SecretKey};
|
use util::secp::key::{PublicKey, SecretKey};
|
||||||
use util::secp::pedersen::ProofMessage;
|
use util::secp::pedersen::ProofMessage;
|
||||||
use util::{kernel_sig_msg, secp};
|
use util::{kernel_sig_msg, secp};
|
||||||
use uuid::Uuid;
|
use wallet::grinwallet::sigcontext;
|
||||||
use wallet::libwallet::{aggsig, proof};
|
use wallet::libwallet::{aggsig, proof};
|
||||||
|
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
@ -33,11 +33,6 @@ use rand::thread_rng;
|
||||||
fn aggsig_sender_receiver_interaction() {
|
fn aggsig_sender_receiver_interaction() {
|
||||||
let sender_keychain = Keychain::from_random_seed().unwrap();
|
let sender_keychain = Keychain::from_random_seed().unwrap();
|
||||||
let receiver_keychain = Keychain::from_random_seed().unwrap();
|
let receiver_keychain = Keychain::from_random_seed().unwrap();
|
||||||
let mut sender_aggsig_cm = aggsig::ContextManager::new();
|
|
||||||
let mut receiver_aggsig_cm = aggsig::ContextManager::new();
|
|
||||||
|
|
||||||
// tx identifier for wallet interaction
|
|
||||||
let tx_id = Uuid::new_v4();
|
|
||||||
|
|
||||||
// Calculate the kernel excess here for convenience.
|
// Calculate the kernel excess here for convenience.
|
||||||
// Normally this would happen during transaction building.
|
// Normally this would happen during transaction building.
|
||||||
|
@ -63,8 +58,10 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let s_cx;
|
||||||
|
let mut rx_cx;
|
||||||
// sender starts the tx interaction
|
// sender starts the tx interaction
|
||||||
let (sender_pub_excess, sender_pub_nonce) = {
|
let (sender_pub_excess, _sender_pub_nonce) = {
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
|
|
||||||
let skey = keychain
|
let skey = keychain
|
||||||
|
@ -80,25 +77,39 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
|
|
||||||
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
||||||
|
|
||||||
let cx = sender_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind);
|
s_cx = sigcontext::Context::new(&keychain.secp(), blind);
|
||||||
cx.get_public_keys(&keychain.secp())
|
s_cx.get_public_keys(&keychain.secp())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pub_nonce_sum;
|
||||||
// receiver receives partial tx
|
// receiver receives partial tx
|
||||||
let (receiver_pub_excess, receiver_pub_nonce, sig_part) = {
|
let (receiver_pub_excess, _receiver_pub_nonce, rx_sig_part) = {
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let key_id = keychain.derive_key_id(1).unwrap();
|
let key_id = keychain.derive_key_id(1).unwrap();
|
||||||
|
|
||||||
// let blind = blind_sum.secret_key(&keychain.secp())?;
|
// let blind = blind_sum.secret_key(&keychain.secp())?;
|
||||||
let blind = keychain.derived_key(&key_id).unwrap();
|
let blind = keychain.derived_key(&key_id).unwrap();
|
||||||
|
|
||||||
let mut cx = receiver_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind);
|
rx_cx = sigcontext::Context::new(&keychain.secp(), blind);
|
||||||
let (pub_excess, pub_nonce) = cx.get_public_keys(&keychain.secp());
|
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||||
cx.add_output(&key_id);
|
rx_cx.add_output(&key_id);
|
||||||
|
|
||||||
let sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0)
|
pub_nonce_sum = PublicKey::from_combination(
|
||||||
.unwrap();
|
keychain.secp(),
|
||||||
receiver_aggsig_cm.save_context(cx);
|
vec![
|
||||||
|
&s_cx.get_public_keys(keychain.secp()).1,
|
||||||
|
&rx_cx.get_public_keys(keychain.secp()).1,
|
||||||
|
],
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let sig_part = aggsig::calculate_partial_sig(
|
||||||
|
&keychain.secp(),
|
||||||
|
&rx_cx.sec_key,
|
||||||
|
&rx_cx.sec_nonce,
|
||||||
|
&pub_nonce_sum,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
).unwrap();
|
||||||
(pub_excess, pub_nonce, sig_part)
|
(pub_excess, pub_nonce, sig_part)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,66 +117,74 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
// received in the response back from the receiver
|
// received in the response back from the receiver
|
||||||
{
|
{
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
let cx = sender_aggsig_cm.get_context(&tx_id);
|
let sig_verifies = aggsig::verify_partial_sig(
|
||||||
let sig_verifies = cx.verify_partial_sig(
|
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
&sig_part,
|
&rx_sig_part,
|
||||||
&receiver_pub_nonce,
|
&pub_nonce_sum,
|
||||||
&receiver_pub_excess,
|
&receiver_pub_excess,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// now sender signs with their key
|
// now sender signs with their key
|
||||||
let sender_sig_part = {
|
let sender_sig_part = {
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
let cx = sender_aggsig_cm.get_context(&tx_id);
|
let sig_part = aggsig::calculate_partial_sig(
|
||||||
cx.calculate_partial_sig(&keychain.secp(), &receiver_pub_nonce, 0, 0)
|
&keychain.secp(),
|
||||||
.unwrap()
|
&s_cx.sec_key,
|
||||||
|
&s_cx.sec_nonce,
|
||||||
|
&pub_nonce_sum,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
).unwrap();
|
||||||
|
sig_part
|
||||||
};
|
};
|
||||||
|
|
||||||
// check the receiver can verify the partial signature
|
// check the receiver can verify the partial signature
|
||||||
// received by the sender
|
// received by the sender
|
||||||
{
|
{
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
let sig_verifies = aggsig::verify_partial_sig(
|
||||||
let sig_verifies = cx.verify_partial_sig(
|
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
&sender_sig_part,
|
&sender_sig_part,
|
||||||
&sender_pub_nonce,
|
&pub_nonce_sum,
|
||||||
&sender_pub_excess,
|
&sender_pub_excess,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receiver now builds final signature from sender and receiver parts
|
// Receiver now builds final signature from sender and receiver parts
|
||||||
let (final_sig, final_pubkey) = {
|
let (final_sig, final_pubkey) = {
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
|
||||||
|
|
||||||
// Receiver recreates their partial sig (we do not maintain state from earlier)
|
let our_sig_part = aggsig::calculate_partial_sig(
|
||||||
let our_sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0)
|
&keychain.secp(),
|
||||||
.unwrap();
|
&rx_cx.sec_key,
|
||||||
|
&rx_cx.sec_nonce,
|
||||||
let combined_nonces = PublicKey::from_combination(
|
&pub_nonce_sum,
|
||||||
keychain.secp(),
|
0,
|
||||||
vec![&sender_pub_nonce, &cx.get_public_keys(keychain.secp()).1],
|
0,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Receiver now generates final signature from the two parts
|
// Receiver now generates final signature from the two parts
|
||||||
let final_sig = cx.calculate_final_sig(
|
let final_sig = aggsig::add_signatures(
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
vec![&sender_sig_part, &our_sig_part],
|
vec![&sender_sig_part, &our_sig_part],
|
||||||
&combined_nonces,
|
&pub_nonce_sum,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Receiver calculates the final public key (to verify sig later)
|
// Receiver calculates the final public key (to verify sig later)
|
||||||
let final_pubkey = cx.calculate_final_pubkey(&keychain.secp(), &sender_pub_excess)
|
let final_pubkey = PublicKey::from_combination(
|
||||||
.unwrap();
|
keychain.secp(),
|
||||||
|
vec![
|
||||||
|
&s_cx.get_public_keys(keychain.secp()).0,
|
||||||
|
&rx_cx.get_public_keys(keychain.secp()).0,
|
||||||
|
],
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
(final_sig, final_pubkey)
|
(final_sig, final_pubkey)
|
||||||
};
|
};
|
||||||
|
@ -173,12 +192,11 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
// Receiver checks the final signature verifies
|
// Receiver checks the final signature verifies
|
||||||
{
|
{
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
|
||||||
|
|
||||||
// Receiver check the final signature verifies
|
// Receiver check the final signature verifies
|
||||||
let sig_verifies =
|
let sig_verifies =
|
||||||
cx.verify_final_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0);
|
aggsig::verify_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check we can verify the sig using the kernel excess
|
// Check we can verify the sig using the kernel excess
|
||||||
|
@ -190,7 +208,7 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
let sig_verifies =
|
let sig_verifies =
|
||||||
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);
|
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);
|
||||||
|
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,11 +216,6 @@ fn aggsig_sender_receiver_interaction() {
|
||||||
fn aggsig_sender_receiver_interaction_offset() {
|
fn aggsig_sender_receiver_interaction_offset() {
|
||||||
let sender_keychain = Keychain::from_random_seed().unwrap();
|
let sender_keychain = Keychain::from_random_seed().unwrap();
|
||||||
let receiver_keychain = Keychain::from_random_seed().unwrap();
|
let receiver_keychain = Keychain::from_random_seed().unwrap();
|
||||||
let mut sender_aggsig_cm = aggsig::ContextManager::new();
|
|
||||||
let mut receiver_aggsig_cm = aggsig::ContextManager::new();
|
|
||||||
|
|
||||||
// tx identifier for wallet interaction
|
|
||||||
let tx_id = Uuid::new_v4();
|
|
||||||
|
|
||||||
// This is the kernel offset that we use to split the key
|
// This is the kernel offset that we use to split the key
|
||||||
// Summing these at the block level prevents the
|
// Summing these at the block level prevents the
|
||||||
|
@ -236,8 +249,10 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let s_cx;
|
||||||
|
let mut rx_cx;
|
||||||
// sender starts the tx interaction
|
// sender starts the tx interaction
|
||||||
let (sender_pub_excess, sender_pub_nonce) = {
|
let (sender_pub_excess, _sender_pub_nonce) = {
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
|
|
||||||
let skey = keychain
|
let skey = keychain
|
||||||
|
@ -256,24 +271,38 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
|
|
||||||
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
let blind = blinding_factor.secret_key(&keychain.secp()).unwrap();
|
||||||
|
|
||||||
let cx = sender_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind);
|
s_cx = sigcontext::Context::new(&keychain.secp(), blind);
|
||||||
cx.get_public_keys(&keychain.secp())
|
s_cx.get_public_keys(&keychain.secp())
|
||||||
};
|
};
|
||||||
|
|
||||||
// receiver receives partial tx
|
// receiver receives partial tx
|
||||||
let (receiver_pub_excess, receiver_pub_nonce, sig_part) = {
|
let pub_nonce_sum;
|
||||||
|
let (receiver_pub_excess, _receiver_pub_nonce, sig_part) = {
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let key_id = keychain.derive_key_id(1).unwrap();
|
let key_id = keychain.derive_key_id(1).unwrap();
|
||||||
|
|
||||||
let blind = keychain.derived_key(&key_id).unwrap();
|
let blind = keychain.derived_key(&key_id).unwrap();
|
||||||
|
|
||||||
let mut cx = receiver_aggsig_cm.create_context(&keychain.secp(), &tx_id, blind);
|
rx_cx = sigcontext::Context::new(&keychain.secp(), blind);
|
||||||
let (pub_excess, pub_nonce) = cx.get_public_keys(&keychain.secp());
|
let (pub_excess, pub_nonce) = rx_cx.get_public_keys(&keychain.secp());
|
||||||
cx.add_output(&key_id);
|
rx_cx.add_output(&key_id);
|
||||||
|
|
||||||
let sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0)
|
pub_nonce_sum = PublicKey::from_combination(
|
||||||
.unwrap();
|
keychain.secp(),
|
||||||
receiver_aggsig_cm.save_context(cx);
|
vec![
|
||||||
|
&s_cx.get_public_keys(keychain.secp()).1,
|
||||||
|
&rx_cx.get_public_keys(keychain.secp()).1,
|
||||||
|
],
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let sig_part = aggsig::calculate_partial_sig(
|
||||||
|
&keychain.secp(),
|
||||||
|
&rx_cx.sec_key,
|
||||||
|
&rx_cx.sec_nonce,
|
||||||
|
&pub_nonce_sum,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
).unwrap();
|
||||||
(pub_excess, pub_nonce, sig_part)
|
(pub_excess, pub_nonce, sig_part)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -281,66 +310,73 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
// received in the response back from the receiver
|
// received in the response back from the receiver
|
||||||
{
|
{
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
let cx = sender_aggsig_cm.get_context(&tx_id);
|
let sig_verifies = aggsig::verify_partial_sig(
|
||||||
let sig_verifies = cx.verify_partial_sig(
|
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
&sig_part,
|
&sig_part,
|
||||||
&receiver_pub_nonce,
|
&pub_nonce_sum,
|
||||||
&receiver_pub_excess,
|
&receiver_pub_excess,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// now sender signs with their key
|
// now sender signs with their key
|
||||||
let sender_sig_part = {
|
let sender_sig_part = {
|
||||||
let keychain = sender_keychain.clone();
|
let keychain = sender_keychain.clone();
|
||||||
let cx = sender_aggsig_cm.get_context(&tx_id);
|
let sig_part = aggsig::calculate_partial_sig(
|
||||||
cx.calculate_partial_sig(&keychain.secp(), &receiver_pub_nonce, 0, 0)
|
&keychain.secp(),
|
||||||
.unwrap()
|
&s_cx.sec_key,
|
||||||
|
&s_cx.sec_nonce,
|
||||||
|
&pub_nonce_sum,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
).unwrap();
|
||||||
|
sig_part
|
||||||
};
|
};
|
||||||
|
|
||||||
// check the receiver can verify the partial signature
|
// check the receiver can verify the partial signature
|
||||||
// received by the sender
|
// received by the sender
|
||||||
{
|
{
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
let sig_verifies = aggsig::verify_partial_sig(
|
||||||
let sig_verifies = cx.verify_partial_sig(
|
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
&sender_sig_part,
|
&sender_sig_part,
|
||||||
&sender_pub_nonce,
|
&pub_nonce_sum,
|
||||||
&sender_pub_excess,
|
&sender_pub_excess,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receiver now builds final signature from sender and receiver parts
|
// Receiver now builds final signature from sender and receiver parts
|
||||||
let (final_sig, final_pubkey) = {
|
let (final_sig, final_pubkey) = {
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
let our_sig_part = aggsig::calculate_partial_sig(
|
||||||
|
&keychain.secp(),
|
||||||
// Receiver recreates their partial sig (we do not maintain state from earlier)
|
&rx_cx.sec_key,
|
||||||
let our_sig_part = cx.calculate_partial_sig(&keychain.secp(), &sender_pub_nonce, 0, 0)
|
&rx_cx.sec_nonce,
|
||||||
.unwrap();
|
&pub_nonce_sum,
|
||||||
|
0,
|
||||||
let combined_nonces = PublicKey::from_combination(
|
0,
|
||||||
keychain.secp(),
|
|
||||||
vec![&sender_pub_nonce, &cx.get_public_keys(keychain.secp()).1],
|
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Receiver now generates final signature from the two parts
|
// Receiver now generates final signature from the two parts
|
||||||
let final_sig = cx.calculate_final_sig(
|
let final_sig = aggsig::add_signatures(
|
||||||
&keychain.secp(),
|
&keychain.secp(),
|
||||||
vec![&sender_sig_part, &our_sig_part],
|
vec![&sender_sig_part, &our_sig_part],
|
||||||
&combined_nonces,
|
&pub_nonce_sum,
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Receiver calculates the final public key (to verify sig later)
|
// Receiver calculates the final public key (to verify sig later)
|
||||||
let final_pubkey = cx.calculate_final_pubkey(&keychain.secp(), &sender_pub_excess)
|
let final_pubkey = PublicKey::from_combination(
|
||||||
.unwrap();
|
keychain.secp(),
|
||||||
|
vec![
|
||||||
|
&s_cx.get_public_keys(keychain.secp()).0,
|
||||||
|
&rx_cx.get_public_keys(keychain.secp()).0,
|
||||||
|
],
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
(final_sig, final_pubkey)
|
(final_sig, final_pubkey)
|
||||||
};
|
};
|
||||||
|
@ -348,12 +384,11 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
// Receiver checks the final signature verifies
|
// Receiver checks the final signature verifies
|
||||||
{
|
{
|
||||||
let keychain = receiver_keychain.clone();
|
let keychain = receiver_keychain.clone();
|
||||||
let cx = receiver_aggsig_cm.get_context(&tx_id);
|
|
||||||
|
|
||||||
// Receiver check the final signature verifies
|
// Receiver check the final signature verifies
|
||||||
let sig_verifies =
|
let sig_verifies =
|
||||||
cx.verify_final_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0);
|
aggsig::verify_sig_build_msg(&keychain.secp(), &final_sig, &final_pubkey, 0, 0);
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check we can verify the sig using the kernel excess
|
// Check we can verify the sig using the kernel excess
|
||||||
|
@ -365,7 +400,7 @@ fn aggsig_sender_receiver_interaction_offset() {
|
||||||
let sig_verifies =
|
let sig_verifies =
|
||||||
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);
|
aggsig::verify_single_from_commit(&keychain.secp(), &final_sig, &msg, &kernel_excess);
|
||||||
|
|
||||||
assert!(sig_verifies);
|
assert!(!sig_verifies.is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ use core::global::ChainTypes;
|
||||||
use core::{global, pow};
|
use core::{global, pow};
|
||||||
use util::LOGGER;
|
use util::LOGGER;
|
||||||
use wallet::grinwallet::selection;
|
use wallet::grinwallet::selection;
|
||||||
use wallet::libwallet::aggsig;
|
|
||||||
|
|
||||||
fn clean_output_dir(test_dir: &str) {
|
fn clean_output_dir(test_dir: &str) {
|
||||||
let _ = fs::remove_dir_all(test_dir);
|
let _ = fs::remove_dir_all(test_dir);
|
||||||
|
@ -57,7 +56,7 @@ fn setup(test_dir: &str, chain_dir: &str) -> Chain {
|
||||||
|
|
||||||
/// Build and test new version of sending API
|
/// Build and test new version of sending API
|
||||||
#[test]
|
#[test]
|
||||||
fn build_transaction_2() {
|
fn build_transaction() {
|
||||||
let chain = setup("test_output", "build_transaction_2/.grin");
|
let chain = setup("test_output", "build_transaction_2/.grin");
|
||||||
let wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1");
|
let wallet1 = common::create_wallet("test_output/build_transaction_2/wallet1");
|
||||||
let wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2");
|
let wallet2 = common::create_wallet("test_output/build_transaction_2/wallet2");
|
||||||
|
@ -68,6 +67,7 @@ fn build_transaction_2() {
|
||||||
// Get lock height
|
// Get lock height
|
||||||
let chain_tip = chain.head().unwrap();
|
let chain_tip = chain.head().unwrap();
|
||||||
let amount = 300_000_000_000;
|
let amount = 300_000_000_000;
|
||||||
|
let min_confirmations = 3;
|
||||||
|
|
||||||
// ensure outputs we're selecting are up to date
|
// ensure outputs we're selecting are up to date
|
||||||
let res = common::refresh_output_state_local(&wallet1.0, &wallet1.1, &chain);
|
let res = common::refresh_output_state_local(&wallet1.0, &wallet1.1, &chain);
|
||||||
|
@ -77,23 +77,20 @@ fn build_transaction_2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TRANSACTION WORKFLOW STARTS HERE
|
// TRANSACTION WORKFLOW STARTS HERE
|
||||||
// Sender creates a new aggsig context
|
|
||||||
let mut sender_context_manager = aggsig::ContextManager::new();
|
|
||||||
// Sender selects outputs into a new slate and save our corresponding IDs in
|
// Sender selects outputs into a new slate and save our corresponding IDs in
|
||||||
// their transaction context. The secret key in our transaction context will be
|
// a transaction context. The secret key in our transaction context will be
|
||||||
// randomly selected. This returns the public slate, and a closure that locks
|
// randomly selected. This returns the public slate, and a closure that locks
|
||||||
// our inputs and outputs once we're convinced the transaction exchange went
|
// our inputs and outputs once we're convinced the transaction exchange went
|
||||||
// according to plan
|
// according to plan
|
||||||
// This function is just a big helper to do all of that, in theory
|
// This function is just a big helper to do all of that, in theory
|
||||||
// this process can be split up in any way
|
// this process can be split up in any way
|
||||||
let (mut slate, sender_lock_fn) = selection::build_send_tx_slate(
|
let (mut slate, mut sender_context, sender_lock_fn) = selection::build_send_tx_slate(
|
||||||
&wallet1.0,
|
&wallet1.0,
|
||||||
&wallet1.1,
|
&wallet1.1,
|
||||||
&mut sender_context_manager,
|
|
||||||
2,
|
2,
|
||||||
amount,
|
amount,
|
||||||
chain_tip.height,
|
chain_tip.height,
|
||||||
3,
|
min_confirmations,
|
||||||
chain_tip.height,
|
chain_tip.height,
|
||||||
1000,
|
1000,
|
||||||
true,
|
true,
|
||||||
|
@ -103,36 +100,44 @@ fn build_transaction_2() {
|
||||||
// the offset in the slate's transaction kernel, and adds our public key
|
// the offset in the slate's transaction kernel, and adds our public key
|
||||||
// information to the slate
|
// information to the slate
|
||||||
let _ = slate
|
let _ = slate
|
||||||
.fill_round_1(&wallet1.1, &mut sender_context_manager, 0)
|
.fill_round_1(
|
||||||
|
&wallet1.1,
|
||||||
|
&mut sender_context.sec_key,
|
||||||
|
&sender_context.sec_nonce,
|
||||||
|
0,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
debug!(LOGGER, "Transaction Slate after step 1: sender initiation");
|
debug!(LOGGER, "Transaction Slate after step 1: sender initiation");
|
||||||
debug!(LOGGER, "-----------------------------------------");
|
debug!(LOGGER, "-----------------------------------------");
|
||||||
debug!(LOGGER, "{:?}", slate);
|
debug!(LOGGER, "{:?}", slate);
|
||||||
|
|
||||||
// RECIPIENT (Handle sender initiation)
|
|
||||||
let mut recipient_context_manager = aggsig::ContextManager::new();
|
|
||||||
|
|
||||||
// Now, just like the sender did, recipient is going to select a target output,
|
// Now, just like the sender did, recipient is going to select a target output,
|
||||||
// add it to the transaction, and keep track of the corresponding wallet
|
// add it to the transaction, and keep track of the corresponding wallet
|
||||||
// Identifier Again, this is a helper to do that, which returns a closure that
|
// Identifier Again, this is a helper to do that, which returns a closure that
|
||||||
// creates the output when we're satisified the process was successful
|
// creates the output when we're satisified the process was successful
|
||||||
let (_, receiver_create_fn) = selection::build_recipient_output_with_slate(
|
let (_, mut recp_context, receiver_create_fn) =
|
||||||
&wallet2.0,
|
selection::build_recipient_output_with_slate(&wallet2.0, &wallet2.1, &mut slate).unwrap();
|
||||||
&wallet2.1,
|
|
||||||
&mut recipient_context_manager,
|
|
||||||
&mut slate,
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let _ = slate
|
let _ = slate
|
||||||
.fill_round_1(&wallet2.1, &mut recipient_context_manager, 1)
|
.fill_round_1(
|
||||||
|
&wallet2.1,
|
||||||
|
&mut recp_context.sec_key,
|
||||||
|
&recp_context.sec_nonce,
|
||||||
|
1,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// recipient can proceed to round 2 now
|
// recipient can proceed to round 2 now
|
||||||
let _ = receiver_create_fn();
|
let _ = receiver_create_fn();
|
||||||
|
|
||||||
let _ = slate
|
let _ = slate
|
||||||
.fill_round_2(&wallet2.1, &mut recipient_context_manager, 1)
|
.fill_round_2(
|
||||||
|
&wallet2.1,
|
||||||
|
&recp_context.sec_key,
|
||||||
|
&recp_context.sec_nonce,
|
||||||
|
1,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -144,7 +149,12 @@ fn build_transaction_2() {
|
||||||
|
|
||||||
// SENDER Part 3: Sender confirmation
|
// SENDER Part 3: Sender confirmation
|
||||||
let _ = slate
|
let _ = slate
|
||||||
.fill_round_2(&wallet1.1, &mut sender_context_manager, 0)
|
.fill_round_2(
|
||||||
|
&wallet1.1,
|
||||||
|
&sender_context.sec_key,
|
||||||
|
&sender_context.sec_nonce,
|
||||||
|
0,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
debug!(LOGGER, "PartialTx after step 3: sender confirmation");
|
debug!(LOGGER, "PartialTx after step 3: sender confirmation");
|
||||||
|
@ -167,7 +177,7 @@ fn build_transaction_2() {
|
||||||
|
|
||||||
// Insert this transaction into a new block, then mine till confirmation
|
// Insert this transaction into a new block, then mine till confirmation
|
||||||
common::award_block_to_wallet(&chain, vec![&slate.tx], &wallet1);
|
common::award_block_to_wallet(&chain, vec![&slate.tx], &wallet1);
|
||||||
common::award_blocks_to_wallet(&chain, &wallet1, 3);
|
common::award_blocks_to_wallet(&chain, &wallet1, 5);
|
||||||
|
|
||||||
// Refresh wallets
|
// Refresh wallets
|
||||||
let res = common::refresh_output_state_local(&wallet2.0, &wallet2.1, &chain);
|
let res = common::refresh_output_state_local(&wallet2.0, &wallet2.1, &chain);
|
||||||
|
@ -175,8 +185,23 @@ fn build_transaction_2() {
|
||||||
panic!("Error refreshing output state for wallet: {:?}", e);
|
panic!("Error refreshing output state for wallet: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check recipient wallet
|
||||||
let chain_tip = chain.head().unwrap();
|
let chain_tip = chain.head().unwrap();
|
||||||
let balances = common::get_wallet_balances(&wallet2.0, &wallet2.1, chain_tip.height).unwrap();
|
let balances = common::get_wallet_balances(&wallet2.0, &wallet2.1, chain_tip.height).unwrap();
|
||||||
|
|
||||||
assert_eq!(balances.3, 300_000_000_000);
|
assert_eq!(balances.3, 300_000_000_000);
|
||||||
|
|
||||||
|
// check sender wallet
|
||||||
|
let res = common::refresh_output_state_local(&wallet1.0, &wallet1.1, &chain);
|
||||||
|
if let Err(e) = res {
|
||||||
|
panic!("Error refreshing output state for wallet: {:?}", e);
|
||||||
|
}
|
||||||
|
let balances = common::get_wallet_balances(&wallet1.0, &wallet1.1, chain_tip.height).unwrap();
|
||||||
|
println!("tip height: {:?}", chain_tip.height);
|
||||||
|
println!("Sender balances: {:?}", balances);
|
||||||
|
// num blocks * grins per block, and wallet1 mined the fee
|
||||||
|
assert_eq!(
|
||||||
|
balances.3,
|
||||||
|
(chain_tip.height - min_confirmations) * 60_000_000_000 - amount
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue