Ensure that prevout, extension_id & mode data can be signed in builder.

This commit is contained in:
Kris Nuttycombe 2020-09-09 10:54:18 -06:00
parent 1a8168e1c9
commit 5a432b5e2b
4 changed files with 41 additions and 31 deletions

View File

@ -354,7 +354,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> {
}
self.txn_builder
.add_tze_input(self.extension_id, prevout, move |_| {
.add_tze_input(self.extension_id, open::MODE, prevout, move |_| {
Ok(Witness::open(preimage_1))
})
.map_err(DemoBuildError::BaseBuilderError)?;
@ -362,7 +362,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> {
self.txn_builder
.add_tze_output(
self.extension_id,
transfer_amount,
transfer_amount,
&Precondition::close(hash_2),
)
.map_err(DemoBuildError::BaseBuilderError)
@ -397,7 +397,7 @@ impl<'a, B: ExtensionTxBuilder<'a>> DemoBuilder<&mut B> {
}
self.txn_builder
.add_tze_input(self.extension_id, prevout, move |_| {
.add_tze_input(self.extension_id, close::MODE, prevout, move |_| {
Ok(Witness::close(preimage_2))
})
.map_err(DemoBuildError::BaseBuilderError)

View File

@ -27,7 +27,7 @@ pub trait ToPayload {
/// A condition that can be used to encumber transparent funds.
///
/// This struct is an intermediate representation between the
/// This struct is an intermediate representation between the
/// serialized binary format which is used inside of a transaction
/// and extension-specific types. The payload field of this struct
/// is treated as opaque to all but extension corresponding to the
@ -51,7 +51,7 @@ impl Precondition {
}
}
/// Attempt to parse an extension-specific precondition value from the
/// Attempt to parse an extension-specific precondition value from the
/// intermediate representation.
pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
P::from_payload(self.mode, &self.payload)
@ -61,7 +61,7 @@ impl Precondition {
/// Data that satisfies the precondition for prior encumbered funds, enabling them to be
/// spent.
///
/// This struct is an intermediate representation between the
/// This struct is an intermediate representation between the
/// serialized binary format which is used inside of a transaction
/// and extension-specific types. The payload field of this struct
/// is treated as opaque to all but extension corresponding to the
@ -85,7 +85,7 @@ impl Witness {
}
}
/// Attempt to parse an extension-specific witness value from the
/// Attempt to parse an extension-specific witness value from the
/// intermediate representation.
pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
P::from_payload(self.mode, &self.payload)
@ -153,10 +153,10 @@ pub trait ExtensionTxBuilder<'a> {
type BuildError;
/// Add a TZE input to the transaction under construction by providing a witness
/// to a precondition identified by a prior outpoint.
/// to a precondition identified by a prior outpoint.
///
/// The `witness_builder` function allows the transaction builder to provide extra
/// contextual information from the transaction under construction to be used
/// contextual information from the transaction under construction to be used
/// in the production of this witness (for example, so that the witness may
/// internally make commitments based upon this information.) For the standard
/// transaction builder, the value provided here is the transaction under
@ -164,6 +164,7 @@ pub trait ExtensionTxBuilder<'a> {
fn add_tze_input<WBuilder, W: ToPayload>(
&mut self,
extension_id: u32,
mode: u32,
prevout: (OutPoint, TzeOut),
witness_builder: WBuilder,
) -> Result<(), Self::BuildError>

View File

@ -51,6 +51,7 @@ pub enum Error {
InvalidAmount,
NoChangeAddress,
SpendProof,
TzeWitnessModeMismatch(u32, u32),
}
impl fmt::Display for Error {
@ -67,6 +68,8 @@ impl fmt::Display for Error {
Error::InvalidAmount => write!(f, "Invalid amount"),
Error::NoChangeAddress => write!(f, "No change address specified or discoverable"),
Error::SpendProof => write!(f, "Failed to create Sapling spend proof"),
Error::TzeWitnessModeMismatch(expected, actual) =>
write!(f, "TZE witness builder returned a mode that did not match the mode with which the input was initially constructed: expected = {:?}, actual = {:?}", expected, actual),
}
}
}
@ -266,7 +269,7 @@ impl TransparentInputs {
struct TzeInputInfo<'a, BuildCtx> {
prevout: TzeOut,
builder: Box<dyn FnOnce(&BuildCtx) -> Result<TzeIn, Error> + 'a>,
builder: Box<dyn FnOnce(&BuildCtx) -> Result<(u32, Vec<u8>), Error> + 'a>,
}
struct TzeInputs<'a, BuildCtx> {
@ -278,27 +281,13 @@ impl<'a, BuildCtx> TzeInputs<'a, BuildCtx> {
TzeInputs { builders: vec![] }
}
fn push<WBuilder, W: ToPayload>(
&mut self,
extension_id: u32,
(outpoint, tzeout): (OutPoint, TzeOut),
builder: WBuilder,
) where
fn push<WBuilder, W: ToPayload>(&mut self, tzeout: TzeOut, builder: WBuilder)
where
WBuilder: 'a + FnOnce(&BuildCtx) -> Result<W, Error>,
{
self.builders.push(TzeInputInfo {
prevout: tzeout,
builder: Box::new(move |ctx| {
let (mode, payload) = builder(&ctx).map(|x| x.to_payload())?;
Ok(TzeIn {
prevout: outpoint,
witness: tze::Witness {
extension_id,
mode,
payload,
},
})
}),
builder: Box::new(move |ctx| builder(&ctx).map(|x| x.to_payload())),
});
}
}
@ -784,7 +773,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
};
// // Create TZE input witnesses
for tze_in in self.tze_inputs.builders {
for (i, tze_in) in self.tze_inputs.builders.into_iter().enumerate() {
// Need to enable witness to commit to the amount.
// - So hardware wallets "know" the amount without having to be sent all the
// prior TZE outputs to which this witness gives evidence.
@ -793,7 +782,13 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
// (Or make it part of the sighash?)
// - TODO: Check whether transparent inputs committing to script_pubkey was
// only so that hardware wallets "knew" what address was being spent from.
self.mtx.tze_inputs.push((tze_in.builder)(&self.mtx)?);
let (mode, payload) = (tze_in.builder)(&self.mtx)?;
let mut current = self.mtx.tze_inputs.get_mut(i).unwrap();
if mode != current.witness.mode {
return Err(Error::TzeWitnessModeMismatch(current.witness.mode, mode));
}
current.witness.payload = payload;
}
// Transparent signatures
@ -816,13 +811,17 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a
fn add_tze_input<WBuilder, W: ToPayload>(
&mut self,
extension_id: u32,
prevout: (OutPoint, TzeOut),
mode: u32,
(outpoint, prevout): (OutPoint, TzeOut),
witness_builder: WBuilder,
) -> Result<(), Self::BuildError>
where
WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result<W, Self::BuildError>),
{
self.tze_inputs.push(extension_id, prevout, witness_builder);
self.mtx
.tze_inputs
.push(TzeIn::new(outpoint, extension_id, mode));
self.tze_inputs.push(prevout, witness_builder);
Ok(())
}

View File

@ -132,6 +132,16 @@ fn to_io_error(_: std::num::TryFromIntError) -> io::Error {
}
impl TzeIn {
pub fn new(prevout: OutPoint, extension_id: u32, mode: u32) -> Self {
TzeIn {
prevout,
witness: tze::Witness {
extension_id,
mode,
payload: vec![],
},
}
}
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let prevout = OutPoint::read(&mut reader)?;