Skip to content

Commit

Permalink
Implement V5 with separate Zebra setup
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeykoren committed Dec 23, 2024
1 parent 767b0a1 commit b0275c9
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 36 deletions.
20 changes: 20 additions & 0 deletions Dockerfile-nu5
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM rust:1.81.0

# Set up Rust and cargo
RUN apt-get update && apt-get install git build-essential clang -y

# Checkout and build custom branch of the zebra repository
ARG branch=zsa-integration-demo
ADD https://api.github.com/repos/QED-it/zebra/git/refs/heads/$branch version.json
RUN git clone -b $branch --single-branch https://github.com/QED-it/zebra.git

WORKDIR zebra

RUN cargo build --release --package zebrad --bin zebrad --features="getblocktemplate-rpcs"

EXPOSE 18232

COPY regtest-config-nu5.toml regtest-config-nu5.toml

# Run the zebra node
ENTRYPOINT ["target/release/zebrad", "-c", "regtest-config-nu5.toml"]
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ docker build -t qedit/zebra-regtest-txv6 .
docker run -p 18232:18232 qedit/zebra-regtest-txv6
```

Or, if you want to test Nu5/V5 Orchard transactions:

```bash
# Build the Zebra Docker image
docker build -f Dockerfile-nu5 -t qedit/zebra-regtest-txv5 .

# Run the Zebra Docker container
docker run -p 18232:18232 qedit/zebra-regtest-txv5
```

For more details on how the Docker image is created and synchronized, refer to the [Dockerfile](./Dockerfile).

### 2. Set Up and Run the Zcash Transaction Tool
Expand Down
22 changes: 22 additions & 0 deletions regtest-config-nu5.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[mining]
miner_address = 'tmLTZegcJN5zaufWQBARHkvqC62mTumm3jR'

[network]
network = "Regtest"

# This section may be omitted when testing only Canopy
[network.testnet_parameters.activation_heights]
# Configured activation heights must be greater than or equal to 1,
# block height 0 is reserved for the Genesis network upgrade in Zebra
NU5 = 1

# This section may be omitted if a persistent Regtest chain state is desired
[state]
ephemeral = true

# This section may be omitted if it's not necessary to send transactions to Zebra's mempool
[rpc]
listen_addr = "0.0.0.0:18232"

# disable cookie auth
enable_cookie_auth = false
30 changes: 24 additions & 6 deletions src/commands/test_orchard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
use abscissa_core::{Command, Runnable};
use orchard::keys::Scope::External;
use orchard::note::AssetBase;
use std::ops::Add;
use zcash_primitives::consensus::BlockHeight;
use zcash_primitives::transaction::TxId;

use crate::commands::test_balances::{check_balances, print_balances, TestBalances};
use crate::components::rpc_client::reqwest::ReqwestRpcClient;
use crate::components::transactions::create_transfer_transaction;
use crate::components::transactions::mine;
use crate::components::transactions::regtest_v5::REGTEST_NETWORK_V5;
use crate::components::transactions::{
create_shield_coinbase_transaction, mine_empty_blocks, sync_from_height,
};
Expand All @@ -31,7 +34,7 @@ impl Runnable for TestOrchardCmd {
let miner = wallet.address_for_account(0, External);
let alice = wallet.address_for_account(1, External);

let coinbase_txid = prepare_test(
let (coinbase_txid, coinbase_value) = prepare_test(
config.chain.nu5_activation_height,
&mut wallet,
&mut rpc_client,
Expand All @@ -42,15 +45,16 @@ impl Runnable for TestOrchardCmd {

// --------------------- Shield miner's reward ---------------------

let shielding_tx = create_shield_coinbase_transaction(miner, coinbase_txid, &mut wallet);
let shielding_tx =
create_shield_coinbase_transaction(miner, coinbase_txid, coinbase_value, &mut wallet);
mine(
&mut wallet,
&mut rpc_client,
Vec::from([shielding_tx]),
false,
);

let expected_delta = TestBalances::new(625_000_000 /*coinbase_reward*/, 0);
let expected_delta = TestBalances::new(coinbase_value as i64, 0);
balances = check_balances(
"=== Balances after shielding ===",
AssetBase::native(),
Expand All @@ -69,6 +73,7 @@ impl Runnable for TestOrchardCmd {
amount_to_transfer_1 as u64,
AssetBase::native(),
&mut wallet,
REGTEST_NETWORK_V5,
);
mine(
&mut wallet,
Expand All @@ -88,9 +93,22 @@ impl Runnable for TestOrchardCmd {
}
}

fn prepare_test(target_height: u32, wallet: &mut User, rpc_client: &mut ReqwestRpcClient) -> TxId {
fn prepare_test(
target_height: u32,
wallet: &mut User,
rpc_client: &mut ReqwestRpcClient,
) -> (TxId, u64) {
sync_from_height(target_height, wallet, rpc_client);
let activate = wallet.last_block_height().is_none();
let (_, coinbase_txid) = mine_empty_blocks(100, rpc_client, activate); // coinbase maturity = 100
coinbase_txid
let (_, coinbase_txid, coinbase_value) = mine_empty_blocks(100, rpc_client, activate); // coinbase maturity = 100
sync_from_height(
wallet
.last_block_height()
.unwrap_or(BlockHeight::from_u32(0))
.add(1)
.into(),
wallet,
rpc_client,
);
(coinbase_txid, coinbase_value)
}
16 changes: 11 additions & 5 deletions src/commands/test_orchard_zsa.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
//! `test` - happy e2e flow that issues, transfers and burns an asset
use abscissa_core::{Command, Runnable};
use orchard::keys::Scope::External;

use crate::commands::test_balances::{check_balances, print_balances, TestBalances};
use crate::components::rpc_client::reqwest::ReqwestRpcClient;
use crate::components::transactions::sync_from_height;
Expand All @@ -11,6 +8,9 @@ use crate::components::transactions::{
};
use crate::components::user::User;
use crate::prelude::*;
use abscissa_core::{Command, Runnable};
use orchard::keys::Scope::External;
use zcash_primitives::consensus::REGTEST_NETWORK;

/// Run the E2E test
#[derive(clap::Parser, Command, Debug)]
Expand Down Expand Up @@ -68,8 +68,14 @@ impl Runnable for TestOrchardZSACmd {

let amount_to_transfer_1 = 3;

let transfer_tx_1 =
create_transfer_transaction(issuer, alice, amount_to_transfer_1, asset, &mut wallet);
let transfer_tx_1 = create_transfer_transaction(
issuer,
alice,
amount_to_transfer_1,
asset,
&mut wallet,
REGTEST_NETWORK,
);
mine(
&mut wallet,
&mut rpc_client,
Expand Down
75 changes: 50 additions & 25 deletions src/components/transactions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
pub(crate) mod regtest_v5;

use crate::components::rpc_client::{BlockProposal, BlockTemplate, RpcClient};
use crate::components::transactions::regtest_v5::REGTEST_NETWORK_V5;
use crate::components::user::User;
use crate::components::zebra_merkle::{
block_commitment_from_parts, AuthDataRoot, Root, AUTH_COMMITMENT_PLACEHOLDER,
Expand All @@ -12,7 +15,9 @@ use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::ops::Add;
use zcash_primitives::block::{BlockHash, BlockHeader, BlockHeaderData};
use zcash_primitives::consensus::{BlockHeight, BranchId, RegtestNetwork, REGTEST_NETWORK};
use zcash_primitives::consensus::{
BlockHeight, BranchId, NetworkUpgrade, Parameters, REGTEST_NETWORK,
};
use zcash_primitives::memo::MemoBytes;
use zcash_primitives::transaction::builder::{BuildConfig, Builder};
use zcash_primitives::transaction::components::amount::NonNegativeAmount;
Expand All @@ -28,7 +33,7 @@ pub fn mine(
txs: Vec<Transaction>,
activate: bool,
) {
let (_, _) = mine_block(rpc_client, txs, activate);
let (_, _, _) = mine_block(rpc_client, txs, activate);
sync(wallet, rpc_client);
}

Expand All @@ -37,48 +42,58 @@ pub fn mine_block(
rpc_client: &mut dyn RpcClient,
txs: Vec<Transaction>,
activate: bool,
) -> (u32, TxId) {
) -> (u32, TxId, u64) {
let block_template = rpc_client.get_block_template().unwrap();
let block_height = block_template.height;

let block_proposal = template_into_proposal(block_template, txs, activate);
let coinbase_txid = block_proposal.transactions.first().unwrap().txid();

let coinbase_value = block_proposal
.transactions
.first()
.unwrap()
.transparent_bundle()
.unwrap()
.vout
.first()
.unwrap()
.value
.into_u64();
rpc_client.submit_block(block_proposal).unwrap();

(block_height, coinbase_txid)
(block_height, coinbase_txid, coinbase_value)
}

/// Mine the given number of empty blocks and return the block height and coinbase txid of the first block
pub fn mine_empty_blocks(
num_blocks: u32,
rpc_client: &mut dyn RpcClient,
activate: bool,
) -> (u32, TxId) {
) -> (u32, TxId, u64) {
if num_blocks == 0 {
panic!("num_blocks must be greater than 0")
}

let (block_height, coinbase_txid) = mine_block(rpc_client, vec![], activate);
let (block_height, coinbase_txid, coinbase_value) = mine_block(rpc_client, vec![], activate);

for _ in 1..num_blocks {
mine_block(rpc_client, vec![], false);
}

(block_height, coinbase_txid)
(block_height, coinbase_txid, coinbase_value)
}

/// Create a shielded coinbase transaction
pub fn create_shield_coinbase_transaction(
recipient: Address,
coinbase_txid: TxId,
coinbase_value: u64,
wallet: &mut User,
) -> Transaction {
info!("Shielding coinbase output from tx {}", coinbase_txid);

let mut tx = create_tx(wallet);
let mut tx = create_tx(wallet, REGTEST_NETWORK_V5);

let coinbase_value = 625_000_000;
let coinbase_amount = NonNegativeAmount::from_u64(coinbase_value).unwrap();
let miner_taddr = wallet.miner_address();

Expand Down Expand Up @@ -118,9 +133,9 @@ pub fn sync(wallet: &mut User, rpc: &mut dyn RpcClient) {
pub fn sync_from_height(from_height: u32, wallet: &mut User, rpc: &mut dyn RpcClient) {
info!("Starting sync from height {}", from_height);

let wallet_last_block_height = wallet.last_block_height().map_or(0, |h| h.into());
let mut next_height = if from_height < wallet_last_block_height {
wallet_last_block_height
let wallet_next_block_height = wallet.last_block_height().map_or(0, |h| h.into()) + 1;
let mut next_height = if from_height < wallet_next_block_height {
wallet_next_block_height
} else {
from_height
};
Expand Down Expand Up @@ -159,12 +174,13 @@ pub fn sync_from_height(from_height: u32, wallet: &mut User, rpc: &mut dyn RpcCl
}

/// Create a transfer transaction
pub fn create_transfer_transaction(
pub fn create_transfer_transaction<N: Parameters>(
sender: Address,
recipient: Address,
amount: u64,
asset: AssetBase,
wallet: &mut User,
network: N,
) -> Transaction {
info!("Transfer {} zatoshi", amount);

Expand All @@ -181,7 +197,7 @@ pub fn create_transfer_transaction(
total_inputs_amount, amount
);

let mut tx = create_tx(wallet);
let mut tx = create_tx(wallet, network);

inputs.into_iter().for_each(|input| {
tx.add_orchard_spend::<FeeError>(&input.sk, input.note, input.merkle_path)
Expand Down Expand Up @@ -235,7 +251,7 @@ pub fn create_burn_transaction(
total_inputs_amount, amount
);

let mut tx = create_tx(wallet);
let mut tx = create_tx(wallet, REGTEST_NETWORK);

inputs.into_iter().for_each(|input| {
tx.add_orchard_spend::<FeeError>(&input.sk, input.note, input.merkle_path)
Expand Down Expand Up @@ -270,7 +286,7 @@ pub fn create_issue_transaction(
wallet: &mut User,
) -> Transaction {
info!("Issue {} asset", amount);
let mut tx = create_tx(wallet);
let mut tx = create_tx(wallet, REGTEST_NETWORK);
tx.init_issuance_bundle::<FeeError>(
wallet.issuance_key(),
asset_desc,
Expand All @@ -286,7 +302,7 @@ pub fn create_issue_transaction(
/// Create a transaction that issues a new asset
pub fn create_finalization_transaction(asset_desc: Vec<u8>, wallet: &mut User) -> Transaction {
info!("Finalize asset");
let mut tx = create_tx(wallet);
let mut tx = create_tx(wallet, REGTEST_NETWORK);
tx.init_issuance_bundle::<FeeError>(wallet.issuance_key(), asset_desc.clone(), None)
.unwrap();
tx.finalize_asset::<FeeError>(asset_desc.as_slice())
Expand Down Expand Up @@ -364,20 +380,29 @@ pub fn template_into_proposal(
}
}

fn create_tx(wallet: &User) -> Builder<'_, RegtestNetwork, ()> {
let build_config = BuildConfig::Zsa {
sapling_anchor: None,
orchard_anchor: wallet.orchard_anchor(),
fn create_tx<N: Parameters>(user: &User, network: N) -> Builder<'_, N, ()> {
let build_config = if network.activation_height(NetworkUpgrade::Nu7).is_none() {
BuildConfig::Standard {
sapling_anchor: None,
orchard_anchor: user.orchard_anchor(),
}
} else {
BuildConfig::Zsa {
sapling_anchor: None,
orchard_anchor: user.orchard_anchor(),
}
};
let tx = Builder::new(
REGTEST_NETWORK,
/*user.last_block_height().unwrap()*/ BlockHeight::from_u32(1_842_420),
network,
user.last_block_height()
.unwrap_or(BlockHeight::from_u32(0))
.add(1),
build_config,
);
tx
}

fn build_tx(builder: Builder<'_, RegtestNetwork, ()>) -> Transaction {
fn build_tx<N: Parameters>(builder: Builder<'_, N, ()>) -> Transaction {
let fee_rule =
&FeeRule::non_standard(NonNegativeAmount::from_u64(0).unwrap(), 20, 150, 34).unwrap();
let prover = LocalTxProver::with_default_location();
Expand Down
27 changes: 27 additions & 0 deletions src/components/transactions/regtest_v5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use zcash_primitives::consensus::{BlockHeight, NetworkType, NetworkUpgrade, Parameters};

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub struct RegtestNetworkV5;

pub const REGTEST_NETWORK_V5: RegtestNetworkV5 = RegtestNetworkV5;

impl Parameters for RegtestNetworkV5 {
fn network_type(&self) -> NetworkType {
NetworkType::Regtest
}

fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
match nu {
NetworkUpgrade::Overwinter => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Sapling => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Blossom => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Heartwood => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Canopy => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Nu5 => Some(BlockHeight::from_u32(1)),
NetworkUpgrade::Nu6 => None,
NetworkUpgrade::Nu7 => None,
#[cfg(zcash_unstable = "zfuture")]
NetworkUpgrade::ZFuture => None,
}
}
}

0 comments on commit b0275c9

Please sign in to comment.