Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save slate participant messages in database #2441

Merged
merged 12 commits into from
Jan 31, 2019
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ chrono = "0.4.4"

grin_keychain = { path = "../keychain", version = "1.0.0" }
grin_util = { path = "../util", version = "1.0.0" }

[dev-dependencies]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone have any major problems with this dev-dependency? Would rather avoid but seems best way to run serialization tests.

serde_json = "1"
2 changes: 1 addition & 1 deletion core/src/libtx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub mod build;
mod error;
pub mod proof;
pub mod reward;
pub mod serialization;
pub mod secp_ser;
pub mod slate;

use crate::consensus;
Expand Down
55 changes: 55 additions & 0 deletions core/src/libtx/serialization.rs → core/src/libtx/secp_ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,58 @@ where
{
serializer.serialize_str(&to_hex(bytes.as_ref().to_vec()))
}

// Test serialization methods of components that are being used
#[cfg(test)]
mod test {
use super::*;
use crate::libtx::aggsig;
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::{Message, Signature};
use crate::util::static_secp_instance;

use serde_json;

use rand::{thread_rng, Rng};

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
struct SerTest {
#[serde(with = "pubkey_serde")]
pub pub_key: PublicKey,
#[serde(with = "option_sig_serde")]
pub opt_sig: Option<Signature>,
#[serde(with = "sig_serde")]
pub sig: Signature,
}

impl SerTest {
pub fn random() -> SerTest {
let static_secp = static_secp_instance();
let secp = static_secp.lock();
let sk = SecretKey::new(&secp, &mut thread_rng());
let mut msg = [0u8; 32];
thread_rng().fill(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
let sig = aggsig::sign_single(&secp, &msg, &sk, None).unwrap();
SerTest {
pub_key: PublicKey::from_secret_key(&secp, &sk).unwrap(),
opt_sig: Some(sig.clone()),
sig: sig.clone(),
}
}
}

#[test]
fn ser_secp_primitives() {
for _ in 0..10 {
let s = SerTest::random();
println!("Before Serialization: {:?}", s);
let serialized = serde_json::to_string_pretty(&s).unwrap();
println!("JSON: {}", serialized);
let deserialized: SerTest = serde_json::from_str(&serialized).unwrap();
println!("After Serialization: {:?}", deserialized);
println!();
assert_eq!(s, deserialized);
}
}
}
47 changes: 45 additions & 2 deletions core/src/libtx/slate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::core::transaction::{kernel_features, kernel_sig_msg, Transaction};
use crate::core::verifier_cache::LruVerifierCache;
use crate::keychain::{BlindSum, BlindingFactor, Keychain};
use crate::libtx::error::{Error, ErrorKind};
use crate::libtx::{aggsig, build, tx_fee};
use crate::libtx::{aggsig, build, secp_ser, tx_fee};
use crate::util::secp;
use crate::util::secp::key::{PublicKey, SecretKey};
use crate::util::secp::Signature;
Expand Down Expand Up @@ -66,6 +66,33 @@ impl ParticipantData {
}
}

/// Public message data (for serialising and storage)
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ParticipantMessageData {
/// id of the particpant in the tx
pub id: u64,
/// Public key
#[serde(with = "secp_ser::pubkey_serde")]
pub public_key: PublicKey,
/// Message,
pub message: Option<String>,
/// Signature
#[serde(with = "secp_ser::option_sig_serde")]
pub message_sig: Option<Signature>,
}

impl ParticipantMessageData {
/// extract relevant message data from participant data
pub fn from_participant_data(p: &ParticipantData) -> ParticipantMessageData {
ParticipantMessageData {
id: p.id,
public_key: p.public_blind_excess,
message: p.message.clone(),
message_sig: p.message_sig.clone(),
}
}
}

/// A 'Slate' is passed around to all parties to build up all of the public
/// transaction data needed to create a finalized transaction. Callers can pass
/// the slate around by whatever means they choose, (but we can provide some
Expand Down Expand Up @@ -94,6 +121,13 @@ pub struct Slate {
pub participant_data: Vec<ParticipantData>,
}

/// Helper just to facilitate serialization
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ParticipantMessages {
/// included messages
pub messages: Vec<ParticipantMessageData>,
}

impl Slate {
/// Create a new slate
pub fn blank(num_participants: usize) -> Slate {
Expand Down Expand Up @@ -274,10 +308,19 @@ impl Slate {
message: message,
message_sig: message_sig,
});

Ok(())
}

/// helper to return all participant messages
pub fn participant_messages(&self) -> ParticipantMessages {
let mut ret = ParticipantMessages { messages: vec![] };
for ref m in self.participant_data.iter() {
ret.messages
.push(ParticipantMessageData::from_participant_data(m));
}
ret
}

/// Somebody involved needs to generate an offset with their private key
/// For now, we'll have the transaction initiator be responsible for it
/// Return offset private key for the participant to use later in the
Expand Down
6 changes: 6 additions & 0 deletions src/bin/cmd/wallet_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,12 @@ mod wallet_tests {
let arg_vec = vec!["grin", "wallet", "-p", "password", "-a", "mining", "txs"];
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;

// message output (mostly spit out for a visual in test logs)
let arg_vec = vec![
"grin", "wallet", "-p", "password", "-a", "mining", "txs", "-i", "10",
];
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;

// txs and outputs (mostly spit out for a visual in test logs)
let arg_vec = vec![
"grin", "wallet", "-p", "password", "-a", "mining", "outputs",
Expand Down
17 changes: 14 additions & 3 deletions wallet/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,14 @@ pub fn owner_api(
) -> Result<(), Error> {
let res = controller::owner_listener(
wallet,
&format!("127.0.0.1:{}", (if global::is_floonet() { "13420" } else { "3420" })),
&format!(
"127.0.0.1:{}",
(if global::is_floonet() {
"13420"
} else {
"3420"
})
),
g_args.node_api_secret.clone(),
g_args.tls_conf.clone(),
config.owner_api_include_foreign.clone(),
Expand Down Expand Up @@ -397,15 +404,19 @@ pub fn txs(
&g_args.account,
height,
validated,
txs,
&txs,
include_status,
dark_scheme,
)?;
// if given a particular transaction id, also get and display associated
// inputs/outputs
// inputs/outputs and messages
if args.id.is_some() {
let (_, outputs) = api.retrieve_outputs(true, false, args.id)?;
display::outputs(&g_args.account, height, validated, outputs, dark_scheme)?;
// should only be one here, but just in case
for tx in txs {
display::tx_messages(&tx, dark_scheme)?;
}
};
Ok(())
})?;
Expand Down
76 changes: 75 additions & 1 deletion wallet/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn txs(
account: &str,
cur_height: u64,
validated: bool,
txs: Vec<TxLogEntry>,
txs: &Vec<TxLogEntry>,
include_status: bool,
dark_background_color_scheme: bool,
) -> Result<(), Error> {
Expand Down Expand Up @@ -357,3 +357,77 @@ pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
table.printstd();
println!();
}

/// Display transaction log messages
pub fn tx_messages(tx: &TxLogEntry, dark_background_color_scheme: bool) -> Result<(), Error> {
let title = format!("Transaction Messages - Transaction '{}'", tx.id,);
println!();
let mut t = term::stdout().unwrap();
t.fg(term::color::MAGENTA).unwrap();
writeln!(t, "{}", title).unwrap();
t.reset().unwrap();

let msgs = match tx.messages.clone() {
None => {
writeln!(t, "{}", "None").unwrap();
t.reset().unwrap();
return Ok(());
}
Some(m) => m.clone(),
};

if msgs.messages.is_empty() {
writeln!(t, "{}", "None").unwrap();
t.reset().unwrap();
return Ok(());
}

let mut table = table!();

table.set_titles(row![
bMG->"Participant Id",
bMG->"Message",
bMG->"Public Key",
bMG->"Signature",
]);

let secp = util::static_secp_instance();
let secp_lock = secp.lock();

for m in msgs.messages {
let id = format!("{}", m.id);
let public_key = format!(
"{}",
util::to_hex(m.public_key.serialize_vec(&secp_lock, true).to_vec())
);
let message = match m.message {
Some(m) => format!("{}", m),
None => "None".to_owned(),
};
let message_sig = match m.message_sig {
Some(s) => format!("{}", util::to_hex(s.serialize_der(&secp_lock))),
None => "None".to_owned(),
};
if dark_background_color_scheme {
table.add_row(row![
bFC->id,
bFC->message,
bFC->public_key,
bFB->message_sig,
]);
} else {
table.add_row(row![
bFD->id,
bFb->message,
bFD->public_key,
bFB->message_sig,
]);
}
}

table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
table.printstd();
println!();

Ok(())
}
2 changes: 1 addition & 1 deletion wallet/src/libwallet/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,12 +685,12 @@ where
let context = w.get_private_context(slate.id.as_bytes())?;
tx::complete_tx(&mut *w, slate, &context)?;
tx::update_stored_tx(&mut *w, slate)?;
tx::update_message(&mut *w, slate)?;
{
let mut batch = w.batch()?;
batch.delete_private_context(slate.id.as_bytes())?;
batch.commit()?;
}

w.close()?;
Ok(())
}
Expand Down
4 changes: 4 additions & 0 deletions wallet/src/libwallet/internal/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ where

let lock_inputs = context.get_inputs().clone();
let _lock_outputs = context.get_outputs().clone();
let messages = Some(slate.participant_messages());

// Return a closure to acquire wallet lock and lock the coins being spent
// so we avoid accidental double spend attempt.
Expand All @@ -122,6 +123,7 @@ where
}

t.amount_debited = amount_debited;
t.messages = messages;

// write the output representing our change
for (change_amount, id, _) in &change_amounts_derivations {
Expand Down Expand Up @@ -195,6 +197,7 @@ where
);

context.add_output(&key_id, &None);
let messages = Some(slate.participant_messages());

// Create closure that adds the output to recipient's wallet
// (up to the caller to decide when to do)
Expand All @@ -206,6 +209,7 @@ where
t.tx_slate_id = Some(slate_id);
t.amount_credited = amount;
t.num_outputs = 1;
t.messages = messages;
batch.save(OutputData {
root_key_id: parent_key_id.clone(),
key_id: key_id_inner.clone(),
Expand Down
23 changes: 22 additions & 1 deletion wallet/src/libwallet/internal/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ where
// create an output using the amount in the slate
let (_, mut context, receiver_create_fn) =
selection::build_recipient_output_with_slate(wallet, slate, parent_key_id.clone())?;

// fill public keys
let _ = slate.fill_round_1(
wallet.keychain(),
Expand All @@ -55,6 +54,8 @@ where
// Save output in wallet
let _ = receiver_create_fn(wallet);

update_message(wallet, slate)?;

Ok(())
}

Expand Down Expand Up @@ -224,6 +225,26 @@ where
Ok(())
}

/// Update the transaction participant messages
pub fn update_message<T: ?Sized, C, K>(wallet: &mut T, slate: &Slate) -> Result<(), Error>
where
T: WalletBackend<C, K>,
C: NodeClient,
K: Keychain,
{
let tx_vec = updater::retrieve_txs(wallet, None, Some(slate.id), None, false)?;
if tx_vec.is_empty() {
return Err(ErrorKind::TransactionDoesntExist(slate.id.to_string()))?;
}
let mut batch = wallet.batch()?;
for tx in tx_vec.into_iter() {
let mut t = tx.clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need t here? we consume tx_vec, could we use tx?

t.messages = Some(slate.participant_messages());
batch.save_tx_log_entry(t.clone(), &t.parent_key_id.clone())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to clone t here?

}
batch.commit()?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd replace 2 lines with just batch.commit();

Ok(())
}
#[cfg(test)]
mod test {
use crate::core::libtx::build;
Expand Down
Loading