Render byte slices as hex more often in `Debug` impls

This is more generally useful for debugging purposes than the default
`Debug` impl for `&[u8]`.

We also provide an alternate `Debug` impl for `legacy::Script` that
parses and renders known opcodes. Note that we only parse a subset of
the full opcode set.
This commit is contained in:
Jack Grigg 2023-05-15 20:45:38 +00:00
parent 57a3914e3a
commit c8e2d81f58
4 changed files with 102 additions and 4 deletions

View File

@ -19,6 +19,8 @@
#![deny(unsafe_code)]
// TODO: #![deny(missing_docs)]
use core::fmt::{self, Write};
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
@ -72,9 +74,28 @@ impl AsRef<[u8]> for OutgoingCipherKey {
/// Newtype representing the byte encoding of an [`EphemeralPublicKey`].
///
/// [`EphemeralPublicKey`]: Domain::EphemeralPublicKey
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct EphemeralKeyBytes(pub [u8; 32]);
impl fmt::Debug for EphemeralKeyBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct HexFmt<'b>(&'b [u8]);
impl<'b> fmt::Debug for HexFmt<'b> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('"')?;
for b in self.0 {
f.write_fmt(format_args!("{:02x}", b))?;
}
f.write_char('"')
}
}
f.debug_tuple("EphemeralKeyBytes")
.field(&HexFmt(&self.0))
.finish()
}
}
impl AsRef<[u8]> for EphemeralKeyBytes {
fn as_ref(&self) -> &[u8] {
&self.0

View File

@ -1,6 +1,8 @@
//! Support for legacy transparent addresses and scripts.
use byteorder::{ReadBytesExt, WriteBytesExt};
use std::fmt;
use std::io::{self, Read, Write};
use std::ops::Shl;
@ -10,6 +12,7 @@ use zcash_encoding::Vector;
pub mod keys;
/// Minimal subset of script opcodes.
#[derive(Debug)]
enum OpCode {
// push value
PushData1 = 0x4c,
@ -28,10 +31,64 @@ enum OpCode {
CheckSig = 0xac,
}
impl OpCode {
fn parse(b: u8) -> Option<Self> {
match b {
0x4c => Some(OpCode::PushData1),
0x4d => Some(OpCode::PushData2),
0x4e => Some(OpCode::PushData4),
0x76 => Some(OpCode::Dup),
0x87 => Some(OpCode::Equal),
0x88 => Some(OpCode::EqualVerify),
0xa9 => Some(OpCode::Hash160),
0xac => Some(OpCode::CheckSig),
_ => None,
}
}
}
/// A serialized script, used inside transparent inputs and outputs of a transaction.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Script(pub Vec<u8>);
impl fmt::Debug for Script {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct ScriptPrinter<'s>(&'s [u8]);
impl<'s> fmt::Debug for ScriptPrinter<'s> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut l = f.debug_list();
let mut unknown: Option<String> = None;
for b in self.0 {
if let Some(opcode) = OpCode::parse(*b) {
if let Some(s) = unknown.take() {
l.entry(&s);
}
l.entry(&opcode);
} else {
let encoded = format!("{:02x}", b);
if let Some(s) = &mut unknown {
s.push_str(&encoded);
} else {
unknown = Some(encoded);
}
}
}
l.finish()
}
}
if f.alternate() {
f.debug_tuple("Script")
.field(&ScriptPrinter(&self.0))
.finish()
} else {
f.debug_tuple("Script")
.field(&hex::encode(&self.0))
.finish()
}
}
}
impl Script {
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let script = Vector::read(&mut reader, |r| r.read_u8())?;

View File

@ -1,4 +1,5 @@
use std::array::TryFromSliceError;
use std::fmt;
use subtle::{Choice, ConstantTimeEq};
@ -9,9 +10,17 @@ use crate::sapling::{
};
/// Typesafe wrapper for nullifier values.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Nullifier(pub [u8; 32]);
impl fmt::Debug for Nullifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Nullifier")
.field(&hex::encode(self.0))
.finish()
}
}
impl Nullifier {
pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
bytes.try_into().map(Nullifier)

View File

@ -7,6 +7,8 @@ use ff::{Field, PrimeField};
use group::GroupEncoding;
use jubjub::{AffinePoint, ExtendedPoint, SubgroupPoint};
use rand_core::RngCore;
use std::fmt;
use std::io::{self, Read, Write};
use std::ops::{AddAssign, MulAssign, Neg};
@ -28,12 +30,21 @@ fn h_star(a: &[u8], b: &[u8]) -> jubjub::Fr {
hash_to_scalar(b"Zcash_RedJubjubH", a, b)
}
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone)]
pub struct Signature {
rbar: [u8; 32],
sbar: [u8; 32],
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Signature")
.field("rbar", &hex::encode(self.rbar))
.field("sbar", &hex::encode(self.sbar))
.finish()
}
}
pub struct PrivateKey(pub jubjub::Fr);
#[derive(Debug, Clone)]