Skip to content

Commit

Permalink
builds, get_block_range not returning correct response
Browse files Browse the repository at this point in the history
  • Loading branch information
idky137 committed Jun 12, 2024
1 parent c6a3174 commit 77f057d
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 180 deletions.
3 changes: 3 additions & 0 deletions integration-tests/tests/integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ mod wallet {

test_manager.regtest_manager.generate_n_blocks(1).unwrap();
zingo_client.do_sync(false).await.unwrap();

// std::thread::sleep(std::time::Duration::from_secs(10));

zingo_client
.do_send(vec![(
&zingolib::get_base_address!(zingo_client, "sapling"),
Expand Down
146 changes: 129 additions & 17 deletions zingo-rpc/src/blockcache/block.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
//! Block fetching and deserialization functionality.
use crate::blockcache::{
transaction::FullTransaction,
utils::{read_bytes, read_i32, read_u32, read_zcash_script_i64, ParseError, ParseFromSlice},
use crate::{
blockcache::{
transaction::FullTransaction,
utils::{
read_bytes, read_i32, read_u32, read_zcash_script_i64, ParseError, ParseFromSlice,
},
},
jsonrpc::{connector::JsonRpcConnector, primitives::GetBlockResponse},
};
use sha2::{Digest, Sha256};
use std::io::Cursor;
use zcash_client_backend::proto::compact_formats::CompactBlock;
use zcash_client_backend::proto::compact_formats::{ChainMetadata, CompactBlock};
use zcash_encoding::CompactSize;

/// A block header, containing metadata about a block.
Expand Down Expand Up @@ -90,7 +95,10 @@ pub struct BlockHeaderData {
}

impl ParseFromSlice for BlockHeaderData {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for BlockHeaderData::parse_from_slice".to_string(),
Expand Down Expand Up @@ -168,7 +176,7 @@ impl BlockHeaderData {
}

/// Extracts the block hash from the block header.
pub fn get_block_hash(&self) -> Result<Vec<u8>, ParseError> {
pub fn get_hash(&self) -> Result<Vec<u8>, ParseError> {
let serialized_header = self.to_binary()?;

let mut hasher = Sha256::new();
Expand Down Expand Up @@ -207,7 +215,10 @@ pub struct FullBlock {
}

impl ParseFromSlice for FullBlock {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
let txid = txid.ok_or_else(|| {
ParseError::InvalidData("txid must be used for FullBlock::parse_from_slice".to_string())
})?;
Expand All @@ -217,6 +228,8 @@ impl ParseFromSlice for FullBlock {
BlockHeaderData::parse_from_slice(&data[cursor.position() as usize..], None)?;
cursor.set_position(data.len() as u64 - remaining_data.len() as u64);

println!("\nBlockHeaderData: {:?}\n", block_header_data);

let tx_count = CompactSize::read(&mut cursor)?;
if txid.len() != tx_count as usize {
return Err(ParseError::InvalidData(format!(
Expand All @@ -240,8 +253,10 @@ impl ParseFromSlice for FullBlock {
transactions.push(tx);
remaining_data = new_remaining_data;
}
println!("\nTransactions: {:?}\n", transactions);

let block_height = Self::get_block_height(&transactions)?;
let block_hash = block_header_data.get_block_hash()?;
let block_hash = block_header_data.get_hash()?;

Ok((
remaining_data,
Expand All @@ -259,7 +274,7 @@ impl ParseFromSlice for FullBlock {

/// Genesis block special case.
///
/// From LoightWalletD:
/// From LightWalletD:
/// see https://github.com/zcash/lightwalletd/issues/17#issuecomment-467110828.
const GENESIS_TARGET_DIFFICULTY: u32 = 520617983;

Expand All @@ -286,21 +301,63 @@ impl FullBlock {
}

/// Decodes a hex encoded zcash full block into a FullBlock struct.
pub fn parse_full_block(data: &[u8], txid: Option<Vec<u8>>) -> Result<Self, ParseError> {
todo!()
pub fn parse_full_block(data: &[u8], txid: Option<Vec<Vec<u8>>>) -> Result<Self, ParseError> {
let (remaining_data, full_block) = Self::parse_from_slice(data, txid)?;
if remaining_data.len() != 0 {
return Err(ParseError::InvalidData(
"Error decoding full block, data ramaining: ({remaining_data})".to_string(),
));
}
Ok(full_block)
}

/// Converts a zcash full block into a compact block.
pub fn to_compact(self) -> Result<CompactBlock, ParseError> {
todo!()
pub fn to_compact(
self,
sapling_commitment_tree_size: u32,
orchard_commitment_tree_size: u32,
) -> Result<CompactBlock, ParseError> {
let vtx = self
.vtx
.into_iter()
.enumerate()
.filter_map(|(index, tx)| {
if tx.has_shielded_elements() {
Some(tx.to_compact(index as u64))
} else {
None
}
})
.collect::<Result<Vec<_>, _>>()?;

let header = self.hdr.raw_block_header.to_binary()?;

let compact_block = CompactBlock {
proto_version: 1, // TODO: check this is correct!
height: self.height as u64,
hash: self.hdr.cached_hash.clone(),
prev_hash: self.hdr.raw_block_header.hash_prev_block.clone(),
time: self.hdr.raw_block_header.time,
header,
vtx,
chain_metadata: Some(ChainMetadata {
sapling_commitment_tree_size,
orchard_commitment_tree_size,
}),
};

Ok(compact_block)
}

/// Decodes a hex encoded zcash full block into a CompactBlock struct.
pub fn parse_to_compact(
data: &[u8],
txid: Option<Vec<u8>>,
txid: Option<Vec<Vec<u8>>>,
sapling_commitment_tree_size: u32,
orchard_commitment_tree_size: u32,
) -> Result<CompactBlock, ParseError> {
todo!()
Ok(Self::parse_full_block(data, txid)?
.to_compact(sapling_commitment_tree_size, orchard_commitment_tree_size)?)
}
}

Expand All @@ -309,6 +366,61 @@ impl FullBlock {
/// Retrieves a full block from zebrad/zcashd using 2 get_block calls.
/// This is because a get_block verbose = 1 call is require to fetch txids.
/// TODO: Save retrieved CompactBlock to the BlockCache.
pub fn get_block_from_node(height: usize) -> Result<CompactBlock, ParseError> {
todo!()
/// TODO: Return more representative error type.
pub async fn get_block_from_node(
zebra_uri: &http::Uri,
height: &u32,
) -> Result<CompactBlock, ParseError> {
let zebrad_client = JsonRpcConnector::new(
zebra_uri.clone(),
Some("xxxxxx".to_string()),
Some("xxxxxx".to_string()),
)
.await;
let block_1 = zebrad_client.get_block(height.to_string(), Some(1)).await;
match block_1 {
Ok(GetBlockResponse::Object {
hash,
confirmations: _,
height: _,
time: _,
tx,
trees,
}) => {
let block_0 = zebrad_client.get_block(hash.0.to_string(), Some(0)).await;
match block_0 {
Ok(GetBlockResponse::Object {
hash: _,
confirmations: _,
height: _,
time: _,
tx: _,
trees: _,
}) => {
return Err(ParseError::InvalidData(
"Received object block type, this should not be possible here.".to_string(),
));
}
Ok(GetBlockResponse::Raw(block_hex)) => {
Ok(FullBlock::parse_to_compact(
block_hex.as_ref(),
Some(tx.into_iter().map(|s| s.into_bytes()).collect()),
0, //trees.sapling as u32,
2, //trees.orchard as u32,
)?)
}
Err(e) => {
return Err(e.into());
}
}
}
Ok(GetBlockResponse::Raw(_)) => {
return Err(ParseError::InvalidData(
"Received raw block type, this should not be possible here.".to_string(),
));
}
Err(e) => {
return Err(e.into());
}
}
}
96 changes: 85 additions & 11 deletions zingo-rpc/src/blockcache/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use crate::blockcache::utils::{
read_bytes, read_u32, read_u64, skip_bytes, ParseError, ParseFromSlice,
};
use std::io::Cursor;
use zcash_client_backend::proto::compact_formats::{CompactBlock, CompactTx};
use zcash_client_backend::proto::compact_formats::{
CompactOrchardAction, CompactSaplingOutput, CompactSaplingSpend, CompactTx,
};
use zcash_encoding::CompactSize;

/// Txin format as described in https://en.bitcoin.it/wiki/Transaction
Expand All @@ -20,7 +22,10 @@ pub struct TxIn {
}

impl ParseFromSlice for TxIn {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for TxIn::parse_from_slice".to_string(),
Expand Down Expand Up @@ -55,7 +60,10 @@ pub struct TxOut {
}

impl ParseFromSlice for TxOut {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for TxOut::parse_from_slice".to_string(),
Expand Down Expand Up @@ -114,7 +122,10 @@ pub struct Spend {
}

impl ParseFromSlice for Spend {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for Spend::parse_from_slice".to_string(),
Expand Down Expand Up @@ -156,7 +167,10 @@ pub struct Output {
}

impl ParseFromSlice for Output {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for Output::parse_from_slice".to_string(),
Expand Down Expand Up @@ -202,7 +216,10 @@ pub struct JoinSplit {
}

impl ParseFromSlice for JoinSplit {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for JoinSplit::parse_from_slice".to_string(),
Expand Down Expand Up @@ -254,7 +271,10 @@ pub struct Action {
}

impl ParseFromSlice for Action {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
if txid != None {
return Err(ParseError::InvalidData(
"txid must be None for Action::parse_from_slice".to_string(),
Expand Down Expand Up @@ -604,7 +624,10 @@ pub struct FullTransaction {
}

impl ParseFromSlice for FullTransaction {
fn parse_from_slice(data: &[u8], txid: Option<Vec<u8>>) -> Result<(&[u8], Self), ParseError> {
fn parse_from_slice(
data: &[u8],
txid: Option<Vec<Vec<u8>>>,
) -> Result<(&[u8], Self), ParseError> {
let txid = txid.ok_or_else(|| {
ParseError::InvalidData(
"txid must be used for FullTransaction::parse_from_slice".to_string(),
Expand Down Expand Up @@ -648,7 +671,7 @@ impl ParseFromSlice for FullTransaction {
let full_transaction = FullTransaction {
raw_transaction: transaction_data,
raw_bytes: data[..(data.len() - remaining_data.len())].to_vec(),
tx_id: txid,
tx_id: txid[0].clone(),
};

Ok((remaining_data, full_transaction))
Expand All @@ -657,7 +680,58 @@ impl ParseFromSlice for FullTransaction {

impl FullTransaction {
/// Converts a zcash full transaction into a compact transaction.
pub fn to_compact(self) -> Result<CompactTx, ParseError> {
todo!()
pub fn to_compact(self, index: u64) -> Result<CompactTx, ParseError> {
let hash = self.tx_id;

// NOTE: LightWalletD currently does not return a fee and is not currently priority here. Please open an Issue or PR at the Zingo-Proxy github (https://github.com/zingolabs/zingo-proxy) if you require this functionality.
let fee = 0;

let spends = self
.raw_transaction
.shielded_spends
.iter()
.map(|spend| CompactSaplingSpend {
nf: spend.nullifier.clone(),
})
.collect();

let outputs = self
.raw_transaction
.shielded_outputs
.iter()
.map(|output| CompactSaplingOutput {
cmu: output.cmu.clone(),
ephemeral_key: output.ephemeral_key.clone(),
ciphertext: output.enc_ciphertext[..52].to_vec(),
})
.collect();

let actions = self
.raw_transaction
.orchard_actions
.iter()
.map(|action| CompactOrchardAction {
nullifier: action.nullifier.clone(),
cmx: action.cmx.clone(),
ephemeral_key: action.ephemeral_key.clone(),
ciphertext: action.enc_ciphertext[..52].to_vec(),
})
.collect();

Ok(CompactTx {
index,
hash,
fee,
spends,
outputs,
actions,
})
}

/// Returns true if the transaction contains either sapling spends or outputs.
pub fn has_shielded_elements(&self) -> bool {
!self.raw_transaction.shielded_spends.is_empty()
|| !self.raw_transaction.shielded_outputs.is_empty()
|| !self.raw_transaction.orchard_actions.is_empty()
}
}
Loading

0 comments on commit 77f057d

Please sign in to comment.