grin/wallet/src/libtx/slate.rs
Ignotus Peverell 0967a5302b
Kernel sum and MMR sizes in block header (#1163)
* Add kernel commitments sum and kernel and output MMR sizes to block header
* Sum a block without including previous sums, cleanup. Blocks are now summed and validated based on their own totals and not the totals since genesis. This allows to get rid of BlockSum and simplified the setting of a new block's roots, kernel sum and MMR sizes. Fixes #116
* Additional kernel MMR validation to check all prior header roots successively
* Wallet tests fix
2018-06-21 02:30:22 +01:00

400 lines
12 KiB
Rust

// 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.
//! Functions for building partial transactions to be passed
//! around during an interactive wallet exchange
use rand::thread_rng;
use uuid::Uuid;
use core::core::committed::Committed;
use core::core::{amount_to_hr_string, Transaction};
use keychain::{BlindSum, BlindingFactor, Keychain};
use libtx::error::{Error, ErrorKind};
use libtx::{aggsig, build, tx_fee};
use util::secp::Signature;
use util::secp::key::{PublicKey, SecretKey};
use util::{secp, LOGGER};
/// Public data for each participant in the slate
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ParticipantData {
/// Id of participant in the transaction. (For now, 0=sender, 1=rec)
pub id: u64,
/// Public key corresponding to private blinding factor
pub public_blind_excess: PublicKey,
/// Public key corresponding to private nonce
pub public_nonce: PublicKey,
/// Public partial signature
pub part_sig: Option<Signature>,
}
impl ParticipantData {
/// A helper to return whether this participant
/// has completed round 1 and round 2;
/// Round 1 has to be completed before instantiation of this struct
/// anyhow, and for each participant consists of:
/// -Inputs added to transaction
/// -Outputs added to transaction
/// -Public signature nonce chosen and added
/// -Public contribution to blinding factor chosen and added
/// Round 2 can only be completed after all participants have
/// performed round 1, and adds:
/// -Part sig is filled out
pub fn is_complete(&self) -> bool {
self.part_sig.is_some()
}
}
/// 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
/// binary or JSON serialization helpers here).
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Slate {
/// The number of participants intended to take part in this transaction
pub num_participants: usize,
/// Unique transaction ID, selected by sender
pub id: Uuid,
/// The core transaction data:
/// inputs, outputs, kernels, kernel offset
pub tx: Transaction,
/// base amount (excluding fee)
pub amount: u64,
/// fee amount
pub fee: u64,
/// Block height for the transaction
pub height: u64,
/// Lock height
pub lock_height: u64,
/// Participant data, each participant in the transaction will
/// insert their public data here. For now, 0 is sender and 1
/// is receiver, though this will change for multi-party
pub participant_data: Vec<ParticipantData>,
}
impl Slate {
/// Create a new slate
pub fn blank(num_participants: usize) -> Slate {
Slate {
num_participants: num_participants,
id: Uuid::new_v4(),
tx: Transaction::empty(),
amount: 0,
fee: 0,
height: 0,
lock_height: 0,
participant_data: vec![],
}
}
/// Adds selected inputs and outputs to the slate's transaction
/// Returns blinding factor
pub fn add_transaction_elements<K>(
&mut self,
keychain: &K,
mut elems: Vec<Box<build::Append<K>>>,
) -> Result<BlindingFactor, Error>
where
K: Keychain,
{
// Append to the exiting transaction
if self.tx.kernels.len() != 0 {
elems.insert(0, build::initial_tx(self.tx.clone()));
}
let (tx, blind) = build::partial_transaction(elems, keychain)?;
self.tx = tx;
Ok(blind)
}
/// Completes callers part of round 1, adding public key info
/// to the slate
pub fn fill_round_1<K>(
&mut self,
keychain: &K,
sec_key: &mut SecretKey,
sec_nonce: &SecretKey,
participant_id: usize,
) -> Result<(), Error>
where
K: Keychain,
{
// Whoever does this first generates the offset
if self.tx.offset == BlindingFactor::zero() {
self.generate_offset(keychain, sec_key)?;
}
self.add_participant_info(keychain, &sec_key, &sec_nonce, participant_id, None)?;
Ok(())
}
/// Completes caller's part of round 2, completing signatures
pub fn fill_round_2<K>(
&mut self,
keychain: &K,
sec_key: &SecretKey,
sec_nonce: &SecretKey,
participant_id: usize,
) -> Result<(), Error>
where
K: Keychain,
{
self.check_fees()?;
self.verify_part_sigs(keychain.secp())?;
let sig_part = aggsig::calculate_partial_sig(
keychain.secp(),
sec_key,
sec_nonce,
&self.pub_nonce_sum(keychain.secp())?,
self.fee,
self.lock_height,
)?;
self.participant_data[participant_id].part_sig = Some(sig_part);
Ok(())
}
/// Creates the final signature, callable by either the sender or recipient
/// (after phase 3: sender confirmation)
/// TODO: Only callable by receiver at the moment
pub fn finalize<K>(&mut self, keychain: &K) -> Result<(), Error>
where
K: Keychain,
{
let final_sig = self.finalize_signature(keychain)?;
self.finalize_transaction(keychain, &final_sig)
}
/// Return the sum of public nonces
fn pub_nonce_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
let pub_nonces = self.participant_data
.iter()
.map(|p| &p.public_nonce)
.collect();
match PublicKey::from_combination(secp, pub_nonces) {
Ok(k) => Ok(k),
Err(e) => Err(ErrorKind::Secp(e))?,
}
}
/// Return the sum of public blinding factors
fn pub_blind_sum(&self, secp: &secp::Secp256k1) -> Result<PublicKey, Error> {
let pub_blinds = self.participant_data
.iter()
.map(|p| &p.public_blind_excess)
.collect();
match PublicKey::from_combination(secp, pub_blinds) {
Ok(k) => Ok(k),
Err(e) => Err(ErrorKind::Secp(e))?,
}
}
/// Return vector of all partial sigs
fn part_sigs(&self) -> Vec<&Signature> {
self.participant_data
.iter()
.map(|p| p.part_sig.as_ref().unwrap())
.collect()
}
/// Adds participants public keys to the slate data
/// and saves participant's transaction context
/// sec_key can be overridden to replace the blinding
/// factor (by whoever split the offset)
fn add_participant_info<K>(
&mut self,
keychain: &K,
sec_key: &SecretKey,
sec_nonce: &SecretKey,
id: usize,
part_sig: Option<Signature>,
) -> Result<(), Error>
where
K: Keychain,
{
// Add our public key and nonce to the slate
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 {
id: id as u64,
public_blind_excess: pub_key,
public_nonce: pub_nonce,
part_sig: part_sig,
});
Ok(())
}
/// 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
/// transaction
fn generate_offset<K>(&mut self, keychain: &K, sec_key: &mut SecretKey) -> Result<(), Error>
where
K: Keychain,
{
// Generate a random kernel offset here
// and subtract it from the blind_sum so we create
// the aggsig context with the "split" key
self.tx.offset =
BlindingFactor::from_secret_key(SecretKey::new(&keychain.secp(), &mut thread_rng()));
let blind_offset = keychain.blind_sum(&BlindSum::new()
.add_blinding_factor(BlindingFactor::from_secret_key(sec_key.clone()))
.sub_blinding_factor(self.tx.offset))?;
*sec_key = blind_offset.secret_key(&keychain.secp())?;
Ok(())
}
/// Checks the fees in the transaction in the given slate are valid
fn check_fees(&self) -> Result<(), Error> {
// double check the fee amount included in the partial tx
// we don't necessarily want to just trust the sender
// we could just overwrite the fee here (but we won't) due to the sig
let fee = tx_fee(
self.tx.inputs.len(),
self.tx.outputs.len(),
self.tx.input_proofs_count(),
None,
);
if fee > self.tx.fee() {
return Err(ErrorKind::Fee(
format!("Fee Dispute Error: {}, {}", self.tx.fee(), fee,).to_string(),
))?;
}
if fee > self.amount + self.fee {
let reason = format!(
"Rejected the transfer because transaction fee ({}) exceeds received amount ({}).",
amount_to_hr_string(fee),
amount_to_hr_string(self.amount + self.fee)
);
info!(LOGGER, "{}", reason);
return Err(ErrorKind::Fee(reason.to_string()))?;
}
Ok(())
}
/// Verifies all of the partial signatures in the Slate are valid
fn verify_part_sigs(&self, secp: &secp::Secp256k1) -> Result<(), Error> {
// collect public nonces
for p in self.participant_data.iter() {
if p.is_complete() {
aggsig::verify_partial_sig(
secp,
p.part_sig.as_ref().unwrap(),
&self.pub_nonce_sum(secp)?,
&p.public_blind_excess,
self.fee,
self.lock_height,
)?;
}
}
Ok(())
}
/// This should be callable by either the sender or receiver
/// once phase 3 is done
///
/// Receive Part 3 of interactive transactions from sender, Sender
/// Confirmation Return Ok/Error
/// -Receiver receives sS
/// -Receiver verifies sender's sig, by verifying that
/// kS * G + e *xS * G = sS* G
/// -Receiver calculates final sig as s=(sS+sR, kS * G+kR * G)
/// -Receiver puts into TX kernel:
///
/// Signature S
/// pubkey xR * G+xS * G
/// fee (= M)
///
/// Returns completed transaction ready for posting to the chain
fn finalize_signature<K>(&mut self, keychain: &K) -> Result<Signature, Error>
where
K: Keychain,
{
self.verify_part_sigs(keychain.secp())?;
let part_sigs = self.part_sigs();
let pub_nonce_sum = self.pub_nonce_sum(keychain.secp())?;
let final_pubkey = self.pub_blind_sum(keychain.secp())?;
// get the final signature
let final_sig = aggsig::add_signatures(&keychain.secp(), part_sigs, &pub_nonce_sum)?;
// Calculate the final public key (for our own sanity check)
// Check our final sig verifies
aggsig::verify_sig_build_msg(
&keychain.secp(),
&final_sig,
&final_pubkey,
self.fee,
self.lock_height,
)?;
Ok(final_sig)
}
/// builds a final transaction after the aggregated sig exchange
fn finalize_transaction<K>(
&mut self,
keychain: &K,
final_sig: &secp::Signature,
) -> Result<(), Error>
where
K: Keychain,
{
let kernel_offset = self.tx.offset;
self.check_fees()?;
let mut final_tx = self.tx.clone();
// build the final excess based on final tx and offset
let final_excess = {
// TODO - do we need to verify rangeproofs here?
for x in &final_tx.outputs {
x.verify_proof()?;
}
// sum the input/output commitments on the final tx
let overage = final_tx.fee() as i64;
let tx_excess = final_tx.sum_commitments(overage)?;
// subtract the kernel_excess (built from kernel_offset)
let offset_excess = keychain
.secp()
.commit(0, kernel_offset.secret_key(&keychain.secp())?)?;
keychain
.secp()
.commit_sum(vec![tx_excess], vec![offset_excess])?
};
// update the tx kernel to reflect the offset excess and sig
assert_eq!(final_tx.kernels.len(), 1);
final_tx.kernels[0].excess = final_excess.clone();
final_tx.kernels[0].excess_sig = final_sig.clone();
// confirm the kernel verifies successfully before proceeding
debug!(LOGGER, "Validating final transaction");
final_tx.kernels[0].verify()?;
// confirm the overall transaction is valid (including the updated kernel)
let _ = final_tx.validate()?;
self.tx = final_tx;
Ok(())
}
}