Allow multiple Aggsig contexts (#685)
* update mean cuda miner to latest trompcode, and added tweakable parameters to grin configuration file * Added UUID for transactions, and store aggsig contexts indexed by transaction ID * updating test framework to allow checking of wallet contents during test
This commit is contained in:
+44
-22
@@ -15,13 +15,40 @@
|
||||
use checker;
|
||||
use keychain::Keychain;
|
||||
use core::core::amount_to_hr_string;
|
||||
use types::{WalletConfig, WalletData, OutputStatus};
|
||||
use types::{WalletConfig, WalletData, OutputStatus, WalletInfo};
|
||||
use prettytable;
|
||||
|
||||
pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
||||
let wallet_info = retrieve_info(config, keychain);
|
||||
println!("\n____ Wallet Summary Info at {} ({}) ____\n",
|
||||
wallet_info.current_height,
|
||||
wallet_info.data_confirmed_from);
|
||||
let mut table = table!(
|
||||
[bFG->"Total", FG->amount_to_hr_string(wallet_info.total)],
|
||||
[bFY->"Awaiting Confirmation", FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation)],
|
||||
[bFY->"Confirmed but Still Locked", FY->amount_to_hr_string(wallet_info.amount_confirmed_but_locked)],
|
||||
[bFG->"Currently Spendable", FG->amount_to_hr_string(wallet_info.amount_currently_spendable)],
|
||||
[Fw->"---------", Fw->"---------"],
|
||||
[Fr->"(Locked by previous transaction)", Fr->amount_to_hr_string(wallet_info.amount_locked)]
|
||||
);
|
||||
table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.printstd();
|
||||
println!();
|
||||
|
||||
if !wallet_info.data_confirmed {
|
||||
println!(
|
||||
"\nWARNING: Failed to verify wallet contents with grin server. \
|
||||
Above info is maybe not fully updated or invalid! \
|
||||
Check that your `grin server` is OK, or see `wallet help restore`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retrieve_info(config: &WalletConfig, keychain: &Keychain)
|
||||
-> WalletInfo {
|
||||
let result = checker::refresh_outputs(&config, &keychain);
|
||||
|
||||
let _ = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
|
||||
let ret_val = WalletData::read_wallet(&config.data_file_dir, |wallet_data| {
|
||||
let (current_height, from) = match checker::get_tip_from_node(config) {
|
||||
Ok(tip) => (tip.height, "from server node"),
|
||||
Err(_) => match wallet_data.outputs.values().map(|out| out.height).max() {
|
||||
@@ -52,25 +79,20 @@ pub fn show_info(config: &WalletConfig, keychain: &Keychain) {
|
||||
}
|
||||
};
|
||||
|
||||
println!("\n____ Wallet Summary Info at {} ({}) ____\n", current_height, from);
|
||||
let mut table = table!(
|
||||
[bFG->"Total", FG->amount_to_hr_string(unspent_total+unconfirmed_total)],
|
||||
[bFY->"Awaiting Confirmation", FY->amount_to_hr_string(unconfirmed_total)],
|
||||
[bFY->"Confirmed but Still Locked", FY->amount_to_hr_string(unspent_but_locked_total)],
|
||||
[bFG->"Currently Spendable", FG->amount_to_hr_string(unspent_total-unspent_but_locked_total)],
|
||||
[Fw->"---------", Fw->"---------"],
|
||||
[Fr->"(Locked by previous transaction)", Fr->amount_to_hr_string(locked_total)]
|
||||
);
|
||||
table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.printstd();
|
||||
println!();
|
||||
let mut data_confirmed = true;
|
||||
if let Err(_) = result {
|
||||
data_confirmed = false;
|
||||
}
|
||||
WalletInfo {
|
||||
current_height : current_height,
|
||||
total: unspent_total+unconfirmed_total,
|
||||
amount_awaiting_confirmation: unconfirmed_total,
|
||||
amount_confirmed_but_locked: unspent_but_locked_total,
|
||||
amount_currently_spendable: unspent_total-unspent_but_locked_total,
|
||||
amount_locked: locked_total,
|
||||
data_confirmed: data_confirmed,
|
||||
data_confirmed_from: String::from(from),
|
||||
}
|
||||
});
|
||||
|
||||
if let Err(_) = result {
|
||||
println!(
|
||||
"\nWARNING: Failed to verify wallet contents with grin server. \
|
||||
Above info is maybe not fully updated or invalid! \
|
||||
Check that your `grin server` is OK, or see `wallet help restore`"
|
||||
);
|
||||
}
|
||||
ret_val.unwrap()
|
||||
}
|
||||
|
||||
+3
-2
@@ -18,6 +18,7 @@ extern crate blake2_rfc as blake2;
|
||||
extern crate byteorder;
|
||||
extern crate rand;
|
||||
extern crate serde;
|
||||
extern crate uuid;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
@@ -53,8 +54,8 @@ pub mod client;
|
||||
pub mod server;
|
||||
|
||||
pub use outputs::show_outputs;
|
||||
pub use info::show_info;
|
||||
pub use info::{show_info, retrieve_info};
|
||||
pub use receiver::{WalletReceiver};
|
||||
pub use sender::{issue_burn_tx, issue_send_tx};
|
||||
pub use types::{BlockFees, CbData, Error, WalletConfig, WalletReceiveRequest, WalletSeed};
|
||||
pub use types::{BlockFees, CbData, Error, WalletConfig, WalletReceiveRequest, WalletInfo, WalletSeed};
|
||||
pub use restore::restore;
|
||||
|
||||
+16
-12
@@ -21,6 +21,7 @@ use iron::prelude::*;
|
||||
use iron::Handler;
|
||||
use iron::status;
|
||||
use serde_json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use api;
|
||||
use core::consensus::reward;
|
||||
@@ -113,13 +114,16 @@ fn handle_sender_initiation(
|
||||
warn!(LOGGER, "Creating new aggsig context");
|
||||
// Create a new aggsig context
|
||||
// this will create a new blinding sum and nonce, and store them
|
||||
keychain.aggsig_create_context(blind_sum.secret_key());
|
||||
keychain.aggsig_add_output(&key_id);
|
||||
let result = keychain.aggsig_create_context(&partial_tx.id, blind_sum.secret_key());
|
||||
if let Err(_) = result {
|
||||
return Err(Error::DuplicateTransactionId);
|
||||
}
|
||||
keychain.aggsig_add_output(&partial_tx.id, &key_id);
|
||||
|
||||
let sig_part=keychain.aggsig_calculate_partial_sig(&sender_pub_nonce, fee, tx.lock_height).unwrap();
|
||||
let sig_part=keychain.aggsig_calculate_partial_sig(&partial_tx.id, &sender_pub_nonce, fee, tx.lock_height).unwrap();
|
||||
|
||||
// Build the response, which should contain sR, blinding excess xR * G, public nonce kR * G
|
||||
let mut partial_tx = build_partial_tx(keychain, amount, Some(sig_part), tx);
|
||||
let mut partial_tx = build_partial_tx(&partial_tx.id, keychain, amount, Some(sig_part), tx);
|
||||
partial_tx.phase = PartialTxPhase::ReceiverInitiation;
|
||||
|
||||
Ok(partial_tx)
|
||||
@@ -144,7 +148,7 @@ fn handle_sender_confirmation(
|
||||
) -> Result<PartialTx, Error> {
|
||||
let (amount, sender_pub_blinding, sender_pub_nonce, sender_sig_part, tx) = read_partial_tx(keychain, partial_tx)?;
|
||||
let sender_sig_part=sender_sig_part.unwrap();
|
||||
let res = keychain.aggsig_verify_partial_sig(&sender_sig_part, &sender_pub_nonce, &sender_pub_blinding, tx.fee, tx.lock_height);
|
||||
let res = keychain.aggsig_verify_partial_sig(&partial_tx.id, &sender_sig_part, &sender_pub_nonce, &sender_pub_blinding, tx.fee, tx.lock_height);
|
||||
|
||||
if !res {
|
||||
error!(LOGGER, "Partial Sig from sender invalid.");
|
||||
@@ -152,13 +156,13 @@ fn handle_sender_confirmation(
|
||||
}
|
||||
|
||||
//Just calculate our sig part again instead of storing
|
||||
let our_sig_part=keychain.aggsig_calculate_partial_sig(&sender_pub_nonce, tx.fee, tx.lock_height).unwrap();
|
||||
let our_sig_part=keychain.aggsig_calculate_partial_sig(&partial_tx.id, &sender_pub_nonce, tx.fee, tx.lock_height).unwrap();
|
||||
|
||||
// And the final signature
|
||||
let final_sig=keychain.aggsig_calculate_final_sig(&sender_sig_part, &our_sig_part, &sender_pub_nonce).unwrap();
|
||||
let final_sig=keychain.aggsig_calculate_final_sig(&partial_tx.id, &sender_sig_part, &our_sig_part, &sender_pub_nonce).unwrap();
|
||||
|
||||
// Calculate the final public key (for our own sanity check)
|
||||
let final_pubkey=keychain.aggsig_calculate_final_pubkey(&sender_pub_blinding).unwrap();
|
||||
let final_pubkey=keychain.aggsig_calculate_final_pubkey(&partial_tx.id, &sender_pub_blinding).unwrap();
|
||||
|
||||
//Check our final sig verifies
|
||||
let res = keychain.aggsig_verify_final_sig_build_msg(&final_sig, &final_pubkey, tx.fee, tx.lock_height);
|
||||
@@ -168,8 +172,7 @@ fn handle_sender_confirmation(
|
||||
return Err(Error::Signature(String::from("Final aggregated signature invalid.")));
|
||||
}
|
||||
|
||||
|
||||
let final_tx = build_final_transaction(config, keychain, amount, &final_sig, tx.clone())?;
|
||||
let final_tx = build_final_transaction(&partial_tx.id, config, keychain, amount, &final_sig, tx.clone())?;
|
||||
let tx_hex = to_hex(ser::ser_vec(&final_tx).unwrap());
|
||||
|
||||
let url = format!("{}/v1/pool/push", config.check_node_api_http_addr.as_str());
|
||||
@@ -177,7 +180,7 @@ fn handle_sender_confirmation(
|
||||
.map_err(|e| Error::Node(e))?;
|
||||
|
||||
// Return what we've actually posted
|
||||
let mut partial_tx = build_partial_tx(keychain, amount, Some(final_sig), tx);
|
||||
let mut partial_tx = build_partial_tx(&partial_tx.id, keychain, amount, Some(final_sig), tx);
|
||||
partial_tx.phase = PartialTxPhase::ReceiverConfirmation;
|
||||
Ok(partial_tx)
|
||||
}
|
||||
@@ -310,6 +313,7 @@ pub fn receive_coinbase(
|
||||
|
||||
/// builds a final transaction after the aggregated sig exchange
|
||||
fn build_final_transaction(
|
||||
tx_id: &Uuid,
|
||||
config: &WalletConfig,
|
||||
keychain: &Keychain,
|
||||
amount: u64,
|
||||
@@ -347,7 +351,7 @@ fn build_final_transaction(
|
||||
|
||||
// Get output we created in earlier step
|
||||
// TODO: will just be one for now, support multiple later
|
||||
let output_vec = keychain.aggsig_get_outputs();
|
||||
let output_vec = keychain.aggsig_get_outputs(tx_id);
|
||||
|
||||
// operate within a lock on wallet data
|
||||
let (key_id, derivation) = WalletData::with_wallet(&config.data_file_dir, |wallet_data| {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use uuid::Uuid;
|
||||
use api;
|
||||
use client;
|
||||
use checker;
|
||||
@@ -61,9 +62,9 @@ pub fn issue_send_tx(
|
||||
*/
|
||||
|
||||
// Create a new aggsig context
|
||||
keychain.aggsig_create_context(blind_sum.secret_key());
|
||||
|
||||
let partial_tx = build_partial_tx(keychain, amount, None, tx);
|
||||
let tx_id = Uuid::new_v4();
|
||||
let _ = keychain.aggsig_create_context(&tx_id, blind_sum.secret_key());
|
||||
let partial_tx = build_partial_tx(&tx_id, keychain, amount, None, tx);
|
||||
|
||||
// Closure to acquire wallet lock and lock the coins being spent
|
||||
// so we avoid accidental double spend attempt.
|
||||
@@ -117,16 +118,16 @@ pub fn issue_send_tx(
|
||||
* -Sender posts sS to receiver
|
||||
*/
|
||||
let (_amount, recp_pub_blinding, recp_pub_nonce, sig, tx) = read_partial_tx(keychain, &res.unwrap())?;
|
||||
let res = keychain.aggsig_verify_partial_sig(&sig.unwrap(), &recp_pub_nonce, &recp_pub_blinding, tx.fee, lock_height);
|
||||
let res = keychain.aggsig_verify_partial_sig(&tx_id, &sig.unwrap(), &recp_pub_nonce, &recp_pub_blinding, tx.fee, lock_height);
|
||||
if !res {
|
||||
error!(LOGGER, "Partial Sig from recipient invalid.");
|
||||
return Err(Error::Signature(String::from("Partial Sig from recipient invalid.")));
|
||||
}
|
||||
|
||||
let sig_part=keychain.aggsig_calculate_partial_sig(&recp_pub_nonce, tx.fee, tx.lock_height).unwrap();
|
||||
let sig_part=keychain.aggsig_calculate_partial_sig(&tx_id, &recp_pub_nonce, tx.fee, tx.lock_height).unwrap();
|
||||
|
||||
// Build the next stage, containing sS (and our pubkeys again, for the recipient's convenience)
|
||||
let mut partial_tx = build_partial_tx(keychain, amount, Some(sig_part), tx);
|
||||
let mut partial_tx = build_partial_tx(&tx_id, keychain, amount, Some(sig_part), tx);
|
||||
partial_tx.phase = PartialTxPhase::SenderConfirmation;
|
||||
|
||||
// And send again
|
||||
|
||||
+33
-2
@@ -15,6 +15,7 @@
|
||||
use blake2;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::{error, fmt, num};
|
||||
use uuid::Uuid;
|
||||
use std::convert::From;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{self, Read, Write};
|
||||
@@ -85,6 +86,11 @@ pub enum Error {
|
||||
Uri(hyper::error::UriError),
|
||||
/// Error with signatures during exchange
|
||||
Signature(String),
|
||||
/// Attempt to use duplicate transaction id in separate transactions
|
||||
DuplicateTransactionId,
|
||||
/// Wallet seed already exists
|
||||
WalletSeedExists,
|
||||
/// Other
|
||||
GenericError(String,)
|
||||
}
|
||||
|
||||
@@ -393,7 +399,7 @@ impl WalletSeed {
|
||||
debug!(LOGGER, "Generating wallet seed file at: {}", seed_file_path,);
|
||||
|
||||
if Path::new(seed_file_path).exists() {
|
||||
panic!("wallet seed file already exists");
|
||||
Err(Error::WalletSeedExists)
|
||||
} else {
|
||||
let seed = WalletSeed::init_new();
|
||||
let mut file = File::create(seed_file_path)?;
|
||||
@@ -707,6 +713,7 @@ pub enum PartialTxPhase {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PartialTx {
|
||||
pub phase: PartialTxPhase,
|
||||
pub id: Uuid,
|
||||
pub amount: u64,
|
||||
pub public_blind_excess: String,
|
||||
pub public_nonce: String,
|
||||
@@ -718,13 +725,14 @@ pub struct PartialTx {
|
||||
/// aggsig_tx_context should contain the private key/nonce pair
|
||||
/// the resulting partial tx will contain the corresponding public keys
|
||||
pub fn build_partial_tx(
|
||||
transaction_id : &Uuid,
|
||||
keychain: &keychain::Keychain,
|
||||
receive_amount: u64,
|
||||
part_sig: Option<secp::Signature>,
|
||||
tx: Transaction,
|
||||
) -> PartialTx {
|
||||
|
||||
let (pub_excess, pub_nonce) = keychain.aggsig_get_public_keys();
|
||||
let (pub_excess, pub_nonce) = keychain.aggsig_get_public_keys(transaction_id);
|
||||
let mut pub_excess = pub_excess.serialize_vec(keychain.secp(), true).clone();
|
||||
let len = pub_excess.clone().len();
|
||||
let pub_excess: Vec<_> = pub_excess.drain(0..len).collect();
|
||||
@@ -735,6 +743,7 @@ pub fn build_partial_tx(
|
||||
|
||||
PartialTx {
|
||||
phase: PartialTxPhase::SenderInitiation,
|
||||
id : transaction_id.clone(),
|
||||
amount: receive_amount,
|
||||
public_blind_excess: util::to_hex(pub_excess),
|
||||
public_nonce: util::to_hex(pub_nonce),
|
||||
@@ -797,3 +806,25 @@ pub struct CbData {
|
||||
pub kernel: String,
|
||||
pub key_id: String,
|
||||
}
|
||||
|
||||
/// a contained wallet info struct, so automated tests can parse wallet info
|
||||
/// can add more fields here over time as needed
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct WalletInfo {
|
||||
// height from which info was taken
|
||||
pub current_height: u64,
|
||||
// total amount in the wallet
|
||||
pub total: u64,
|
||||
// amount awaiting confirmation
|
||||
pub amount_awaiting_confirmation: u64,
|
||||
// confirmed but locked
|
||||
pub amount_confirmed_but_locked: u64,
|
||||
// amount currently spendable
|
||||
pub amount_currently_spendable: u64,
|
||||
// amount locked by previous transactions
|
||||
pub amount_locked: u64,
|
||||
// whether the data was confirmed against a live node
|
||||
pub data_confirmed: bool,
|
||||
// node confirming the data
|
||||
pub data_confirmed_from: String,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user