-
Notifications
You must be signed in to change notification settings - Fork 242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block header parsing and transaction ID computation #66
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just fun complaints, nothing urgent. Nice PR!
@@ -501,6 +576,7 @@ dependencies = [ | |||
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe" | |||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" | |||
"checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7" | |||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're bringing in two different versions of this crate apparently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The older version is coming in through the aes
crate, which is used via the fpe
by zip32
. We can bump fpe
to the latest aes
crate, which will resolve this discrepancy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened str4d/fpe#9 with this change, along with some minor cleanups and improvements. I've confirmed that it reduces the number of dependencies by three (two of which were older versions of crates we still depend on).
@@ -8,10 +8,12 @@ authors = [ | |||
[dependencies] | |||
byteorder = "1" | |||
ff = { path = "../ff" } | |||
hex = "0.3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zcash_primitives/src/block.rs
Outdated
|
||
impl fmt::Display for BlockHash { | ||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
let mut data = self.0.to_vec(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead, you could initialize a local clone of the self.0: [u8; 32]
and then [T]::reverse()
on that, rather than allocating.
(Not blocking.)
|
||
impl fmt::Display for TxId { | ||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
let mut data = self.0.to_vec(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as the other review comment about something similar to this.
Rebased the last few commits again because I forgot to correct the Cargo.lock after rebasing. |
use std::ops::Deref; | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
pub struct BlockHash(pub [u8; 32]); | ||
|
||
impl fmt::Display for BlockHash { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside from the name, BlockHash
is the same as TxId
and in this commit we write the same fn fmt
twice. This makes me wonder if there should be a single type (named ByteArray32
or something like that) rather than two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced that the extra indirection to the underlying bytes is worth it yet. If we find the reverse-hex format is bleeding in too much from zcashd
then we can revisit this.
} | ||
} | ||
|
||
impl Transaction { | ||
fn from_data(data: TransactionData) -> io::Result<Self> { | ||
let mut tx = Transaction(data, TxId([0; 32])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels we are going back and forth a bit having to construct the transaction and write it's data to an array before we can set the txid. It would be possible to do this all at once, but maybe it's not worth the trouble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By design, I chose to only allow serialization of complete transactions (so that the transaction format is not used for storing "partially-complete transactions", which greatly complicates the job of the transaction parser). Thus it is necessary either to do what we do here, or to have the transaction serializer be a standalone function that takes all the transaction components as inputs. I don't think the complexity of the latter is worth it; the txid
field of Transaction
is private, so we know that only this module (and submodules) will be able to see and/or modify it directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be done by moving the write<W: Write>(&self, mut writer: W)
function from Transaction
to TransactionData
, but I could see this being beyond the scope of this PR and we can revisit it later.
pub version: i32, | ||
pub prev_block: BlockHash, | ||
pub merkle_root: [u8; 32], | ||
pub final_sapling_root: [u8; 32], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this is out of scope for the PR, but the BlockHash
is a typed wrapper for a [u8; 32]
; are the merkle_root
and final_sapling_root
comparable 32-byte arrays, or semantically-different types? Should they also be newtype wrappers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are all hash outputs, but of at least two different hash functions. In zcashd
they are represented by the same 256-bit binary blob type, which has the "feature" that when rendered in hex in zcashd
RPC output, the bytes are reversed (behaviour inherited from Bitcoin Core).
I use separate types for block hashes and transaction IDs because those are the only two that external code is likely to actually interact with, whereas other block header fields (thus far) will only be used internally. Once the refactor is further along and we get to the stage of cleaning up the crate APIs, we should revisit this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #73.
reader.read_exact(&mut merkle_root)?; | ||
|
||
let mut final_sapling_root = [0; 32]; | ||
reader.read_exact(&mut final_sapling_root)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's anything wrong with this code, but I get a little anxious about the let mut bytes = zeros; read bytes;
pattern, since (considering the reading, not the zeroing, as the "real" initialization) the variable initialization is unlinked from its declaration, and the resulting variable is left mutable even if it doesn't need to be.
Since reading 32 bytes is a pretty common idiom, maybe it would make sense to define some extension behaviour like so:
pub trait ReadExt {
fn read_32_bytes(&mut self) -> io::Result<[u8; 32]>;
}
impl<R: Read> ReadExt for R {
#[inline]
fn read_32_bytes(&mut self) -> io::Result<[u8; 32]> {
let mut bytes = [0; 32];
self.read_exact(&mut bytes)?;
Ok(bytes)
}
}
so that this byte-handling code can become, e.g.,
let prev_block = BlockHash(reader.read_32_bytes()?);
let merkle_root = reader.read_32_bytes()?;
let final_sapling_root = reader.read_32_bytes()?;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a commonly-used idiom throughout the existing codebase, so we should address this comprehensively. Opened #72
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK. My comments are non-blocking.
No description provided.