Slight changes to transaction signature. The transaction fee gets signed (instead of the empty string) and the fee amount is attached to the transaction proof in blocks. This allows fee accounting and the tracking of fees within a block.

This commit is contained in:
Ignotus Peverell 2016-11-09 11:05:44 -08:00
parent 2e23c64448
commit 8317854b70
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
2 changed files with 33 additions and 14 deletions

View file

@ -340,11 +340,8 @@ impl Block {
} }
// verify all signatures with the commitment as pk // verify all signatures with the commitment as pk
let msg = try!(Message::from_slice(&[0; 32]));
for proof in &self.proofs { for proof in &self.proofs {
let pubk = try!(proof.remainder.to_pubkey(secp)); try!(proof.verify(secp));
let sig = try!(Signature::from_der(secp, &proof.sig));
try!(secp.verify(&msg, &sig, &pubk));
} }
Ok(()) Ok(())
} }
@ -368,6 +365,7 @@ impl Block {
let proof = TxProof { let proof = TxProof {
remainder: remainder, remainder: remainder,
sig: sig.serialize_der(&secp), sig: sig.serialize_der(&secp),
fee: 0,
}; };
Ok((output, proof)) Ok((output, proof))
} }

View file

@ -17,44 +17,58 @@
use core::Committed; use core::Committed;
use core::MerkleRow; use core::MerkleRow;
use core::hash::{Hash, Hashed}; use core::hash::{Hash, Hashed};
use ser::{self, Reader, Writer, Readable, Writeable};
use byteorder::{ByteOrder, BigEndian};
use secp::{self, Secp256k1, Message, Signature}; use secp::{self, Secp256k1, Message, Signature};
use secp::key::SecretKey; use secp::key::SecretKey;
use secp::pedersen::{RangeProof, Commitment}; use secp::pedersen::{RangeProof, Commitment};
use ser::{self, Reader, Writer, Readable, Writeable};
/// The maximum number of inputs or outputs a transaction may have /// The maximum number of inputs or outputs a transaction may have
/// and be deserializable. /// and be deserializable.
pub const MAX_IN_OUT_LEN: u64 = 50000; pub const MAX_IN_OUT_LEN: u64 = 50000;
/// A proof that a transaction did not create (or remove) funds. Includes both /// A proof that a transaction sums to zero. Includes both the transaction's Pedersen commitment and the signature, that guarantees that the commitments amount to zero. The signature signs the fee, which is retained for signature validation.
/// the transaction's Pedersen commitment and the signature that guarantees
/// that the commitment amounts to zero.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TxProof { pub struct TxProof {
/// temporarily public /// Remainder of the sum of all transaction commitments. If the transaction is well formed, amounts components should sum to zero and the remainder is hence a valid public key.
pub remainder: Commitment, pub remainder: Commitment,
/// temporarily public /// The signature proving the remainder is a valid public key, which signs the transaction fee.
pub sig: Vec<u8>, pub sig: Vec<u8>,
/// Fee originally included in the transaction this proof is for.
pub fee: u64,
} }
impl Writeable for TxProof { impl Writeable for TxProof {
fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> { fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> {
try!(writer.write_fixed_bytes(&self.remainder)); try!(writer.write_fixed_bytes(&self.remainder));
writer.write_bytes(&self.sig) try!(writer.write_bytes(&self.sig));
writer.write_u64(self.fee)
} }
} }
impl Readable<TxProof> for TxProof { impl Readable<TxProof> for TxProof {
fn read(reader: &mut Reader) -> Result<TxProof, ser::Error> { fn read(reader: &mut Reader) -> Result<TxProof, ser::Error> {
let (remainder, sig) = ser_multiread!(reader, read_33_bytes, read_vec); let (remainder, sig, fee) = ser_multiread!(reader, read_33_bytes, read_vec, read_u64);
Ok(TxProof { Ok(TxProof {
remainder: Commitment::from_vec(remainder), remainder: Commitment::from_vec(remainder),
sig: sig, sig: sig,
fee: fee,
}) })
} }
} }
impl TxProof {
/// Verify the transaction proof validity. Entails handling the commitment as a public key and checking the signature verifies with the fee as message.
pub fn verify(&self, secp: &Secp256k1) -> Result<(), secp::Error> {
let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee)));
let pubk = try!(self.remainder.to_pubkey(secp));
let sig = try!(Signature::from_der(secp, &self.sig));
secp.verify(&msg, &sig, &pubk)
}
}
/// A transaction /// A transaction
#[derive(Debug)] #[derive(Debug)]
pub struct Transaction { pub struct Transaction {
@ -175,7 +189,7 @@ impl Transaction {
// and sign with the remainder so the signature can be checked to match with // and sign with the remainder so the signature can be checked to match with
// the k.G commitment leftover, that should also be the pubkey // the k.G commitment leftover, that should also be the pubkey
let msg = try!(Message::from_slice(&[0; 32])); let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee)));
let sig = try!(secp.sign(&msg, &remainder)); let sig = try!(secp.sign(&msg, &remainder));
Ok(Transaction { Ok(Transaction {
@ -208,13 +222,14 @@ impl Transaction {
// pretend the sum is a public key (which it is, being of the form r.G) and // pretend the sum is a public key (which it is, being of the form r.G) and
// verify the transaction sig with it // verify the transaction sig with it
let pubk = try!(rsum.to_pubkey(secp)); let pubk = try!(rsum.to_pubkey(secp));
let msg = try!(Message::from_slice(&[0; 32])); let msg = try!(Message::from_slice(&u64_to_32bytes(self.fee)));
let sig = try!(Signature::from_der(secp, &self.zerosig)); let sig = try!(Signature::from_der(secp, &self.zerosig));
try!(secp.verify(&msg, &sig, &pubk)); try!(secp.verify(&msg, &sig, &pubk));
Ok(TxProof { Ok(TxProof {
remainder: rsum, remainder: rsum,
sig: self.zerosig.clone(), sig: self.zerosig.clone(),
fee: self.fee,
}) })
} }
} }
@ -368,6 +383,12 @@ pub fn merkle_inputs_outputs(inputs: &Vec<Input>, outputs: &Vec<Output>) -> Hash
MerkleRow::new(all_hs).root() MerkleRow::new(all_hs).root()
} }
fn u64_to_32bytes(n: u64) -> [u8; 32] {
let mut bytes = [0; 32];
BigEndian::write_u64(&mut bytes[24..32], n);
bytes
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;