Skip to content

Commit

Permalink
Save slate participant messages in database (#2441)
Browse files Browse the repository at this point in the history
* Save and display optional slate messages

* rustfmt

* fixes and test updates

* rustfmt

* output json

* rustfmt

* add better serialisation of slate message, rename to secp_ser, add tests for serialization functions

* rustfmt

* max length for message enforced by API

* rustfmt
  • Loading branch information
yeastplume authored Jan 31, 2019
1 parent 422db82 commit 0c851c5
Show file tree
Hide file tree
Showing 13 changed files with 288 additions and 11 deletions.
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]
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, Wei
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
18 changes: 17 additions & 1 deletion src/bin/cmd/wallet_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ mod wallet_tests {
let mut bh = 10u64;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), bh as usize);

let very_long_message = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef\
This part should all be truncated";

// Update info and check
let arg_vec = vec!["grin", "wallet", "-p", "password", "-a", "mining", "info"];
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
Expand All @@ -273,7 +283,7 @@ mod wallet_tests {
"-d",
&file_name,
"-g",
"Love, Yeast",
very_long_message,
"10",
];
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
Expand Down Expand Up @@ -491,6 +501,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
8 changes: 6 additions & 2 deletions wallet/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,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(())
}
24 changes: 22 additions & 2 deletions wallet/src/libwallet/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ use crate::libwallet::{Error, ErrorKind};
use crate::util;
use crate::util::secp::{pedersen, ContextFlag, Secp256k1};

const USER_MESSAGE_MAX_LEN: usize = 256;

/// Functions intended for use by the owner (e.g. master seed holder) of the wallet.
pub struct APIOwner<W: ?Sized, C, K>
where
Expand Down Expand Up @@ -551,7 +553,8 @@ where
/// ParticipantData within the slate. This message will include a signature created with the
/// sender's private keys, and will be publically verifiable. Note this message is for
/// the convenience of the participants during the exchange; it is not included in the final
/// transaction sent to the chain. Validation of this message is optional.
/// transaction sent to the chain. The message will be truncated to 256 characters.
/// Validation of this message is optional.
///
/// # Returns
/// * a result containing:
Expand Down Expand Up @@ -640,6 +643,14 @@ where
None => w.parent_key_id(),
};

let message = match message {
Some(mut m) => {
m.truncate(USER_MESSAGE_MAX_LEN);
Some(m)
}
None => None,
};

let (slate, context, lock_fn) = tx::create_send_tx(
&mut *w,
amount,
Expand Down Expand Up @@ -685,12 +696,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 Expand Up @@ -873,6 +884,15 @@ where
return Err(ErrorKind::TransactionAlreadyReceived(slate.id.to_string()).into());
}
}

let message = match message {
Some(mut m) => {
m.truncate(USER_MESSAGE_MAX_LEN);
Some(m)
}
None => None,
};

let res = tx::receive_tx(&mut *w, slate, &parent_key_id, message);
w.close()?;

Expand Down
Loading

0 comments on commit 0c851c5

Please sign in to comment.