#![allow(dead_code)]
pub use sapling_crypto_note::SaplingCryptoNoteBuilder;
fn zaddr_from_seed(
seed: [u8; 32],
) -> (
ExtendedSpendingKey,
PreparedIncomingViewingKey,
PaymentAddress,
) {
let extsk = ExtendedSpendingKey::master(&seed);
let dfvk = extsk.to_diversifiable_full_viewing_key();
let fvk = dfvk;
let (_, addr) = fvk.default_address();
(
extsk,
PreparedIncomingViewingKey::new(&fvk.fvk().vk.ivk()),
addr,
)
}
pub fn default_txid() -> zcash_primitives::transaction::TxId {
zcash_primitives::transaction::TxId::from_bytes([0u8; 32])
}
pub fn default_zaddr() -> (
ExtendedSpendingKey,
PreparedIncomingViewingKey,
PaymentAddress,
) {
zaddr_from_seed([0u8; 32])
}
use rand::{Rng, rngs::OsRng};
use sapling_crypto::{
PaymentAddress, note_encryption::PreparedIncomingViewingKey, zip32::ExtendedSpendingKey,
};
pub fn random_txid() -> zcash_primitives::transaction::TxId {
let mut rng = OsRng;
let mut seed = [0u8; 32];
rng.fill(&mut seed);
zcash_primitives::transaction::TxId::from_bytes(seed)
}
pub fn random_zaddr() -> (
ExtendedSpendingKey,
PreparedIncomingViewingKey,
PaymentAddress,
) {
let mut rng = OsRng;
let mut seed = [0u8; 32];
rng.fill(&mut seed);
zaddr_from_seed(seed)
}
pub mod nullifier {
use crate::utils::build_method;
macro_rules! build_assign_unique_nullifier {
() => {
pub(crate) fn assign_unique_nullifier(&mut self) -> &mut Self {
if let Some(last) = self.unique_nullifier.last_mut() {
if *last == u8::MAX {
panic!("maximum unique nullifiers reached!");
}
*last += 1;
}
self.nullifier = Some(self.unique_nullifier);
self
}
};
}
#[derive(Clone)]
pub(crate) struct SaplingNullifierBuilder {
unique_nullifier: [u8; 32],
nullifier: Option<[u8; 32]>,
}
impl SaplingNullifierBuilder {
pub(crate) fn new() -> Self {
SaplingNullifierBuilder {
unique_nullifier: [0u8; 32],
nullifier: None,
}
}
build_method!(nullifier, [u8; 32]);
build_assign_unique_nullifier!();
pub fn build(&self) -> sapling_crypto::Nullifier {
sapling_crypto::Nullifier::from_slice(&self.nullifier.unwrap()).unwrap()
}
}
impl Default for SaplingNullifierBuilder {
fn default() -> Self {
let mut builder = Self::new();
builder.nullifier([0u8; 32]);
builder
}
}
#[derive(Clone)]
pub(crate) struct OrchardNullifierBuilder {
unique_nullifier: [u8; 32],
nullifier: Option<[u8; 32]>,
}
impl OrchardNullifierBuilder {
pub(crate) fn new() -> Self {
OrchardNullifierBuilder {
unique_nullifier: [0u8; 32],
nullifier: None,
}
}
build_method!(nullifier, [u8; 32]);
build_assign_unique_nullifier!();
pub fn build(&self) -> orchard::note::Nullifier {
orchard::note::Nullifier::from_bytes(&self.nullifier.unwrap()).unwrap()
}
}
impl Default for OrchardNullifierBuilder {
fn default() -> Self {
let mut builder = Self::new();
builder.nullifier([0u8; 32]);
builder
}
}
}
mod sapling_crypto_note {
use sapling_crypto::Note;
use sapling_crypto::PaymentAddress;
use sapling_crypto::Rseed;
use sapling_crypto::value::NoteValue;
use crate::utils::build_method;
use super::default_zaddr;
#[derive(Clone)]
pub struct SaplingCryptoNoteBuilder {
recipient: Option<PaymentAddress>,
value: Option<NoteValue>,
rseed: Option<Rseed>,
}
impl SaplingCryptoNoteBuilder {
pub fn new() -> Self {
SaplingCryptoNoteBuilder {
recipient: None,
value: None,
rseed: None,
}
}
build_method!(recipient, PaymentAddress);
build_method!(value, NoteValue);
build_method!(rseed, Rseed);
pub fn randomize_recipient(&mut self) -> &mut Self {
let (_, _, address) = super::random_zaddr();
self.recipient(address)
}
pub fn build(self) -> Note {
Note::from_parts(
self.recipient.unwrap(),
self.value.unwrap(),
self.rseed.unwrap(),
)
}
}
impl Default for SaplingCryptoNoteBuilder {
fn default() -> Self {
let (_, _, address) = default_zaddr();
let mut builder = Self::new();
builder
.recipient(address)
.value(NoteValue::from_raw(200_000))
.rseed(Rseed::AfterZip212([7; 32]));
builder
}
}
}
pub mod orchard_note {
use orchard::{
Address, Note,
keys::{FullViewingKey, SpendingKey},
note::{RandomSeed, Rho},
value::NoteValue,
};
use rand::{Rng, rngs::OsRng};
use zip32::Scope;
use crate::utils::build_method;
#[derive(Clone)]
pub struct OrchardCryptoNoteBuilder {
recipient: Option<Address>,
value: Option<NoteValue>,
rho: Option<Rho>,
random_seed: Option<RandomSeed>,
}
impl OrchardCryptoNoteBuilder {
pub fn new() -> Self {
OrchardCryptoNoteBuilder {
recipient: None,
value: None,
rho: None,
random_seed: None,
}
}
build_method!(recipient, Address);
build_method!(value, NoteValue);
build_method!(rho, Rho);
build_method!(random_seed, RandomSeed);
pub fn default_recipient(&mut self) -> &mut Self {
let bytes = [0; 32];
let sk = SpendingKey::from_bytes(bytes).unwrap();
let fvk: FullViewingKey = (&sk).into();
let recipient = fvk.address_at(0u32, Scope::External);
self.recipient(recipient)
}
pub fn randomize_recipient(&mut self) -> &mut Self {
let mut rng = OsRng;
let sk = {
loop {
let mut bytes = [0; 32];
rng.fill(&mut bytes);
let sk = SpendingKey::from_bytes(bytes);
if sk.is_some().into() {
break sk.unwrap();
}
}
};
let fvk: FullViewingKey = (&sk).into();
let recipient = fvk.address_at(0u32, Scope::External);
self.recipient(recipient)
}
pub fn randomize_rho_and_rseed(&mut self) -> &mut Self {
let mut rng = OsRng;
let rho = {
loop {
let mut bytes = [0u8; 32];
rng.fill(&mut bytes);
let rho = Rho::from_bytes(&bytes);
if rho.is_some().into() {
break rho.unwrap();
}
}
};
let random_seed = {
loop {
let mut bytes = [0; 32];
rng.fill(&mut bytes);
let random_seed = RandomSeed::from_bytes(bytes, &rho);
if random_seed.is_some().into() {
break random_seed.unwrap();
}
}
};
self.rho(rho).random_seed(random_seed)
}
pub fn build(&self) -> Note {
Note::from_parts(
self.recipient.unwrap(),
self.value.unwrap(),
self.rho.unwrap(),
self.random_seed.unwrap(),
)
.unwrap()
}
pub fn non_random(nonce: [u8; 32]) -> Self {
fn next_valid_thing<T>(mut nonce: [u8; 32], f: impl Fn([u8; 32]) -> Option<T>) -> T {
let mut i = 0;
loop {
if let Some(output) = f(nonce) {
return output;
} else {
nonce[i % 32] = nonce[i % 32].wrapping_add(1);
i += 1;
}
}
}
let rho = next_valid_thing(nonce, |bytes| Option::from(Rho::from_bytes(&bytes)));
let rseed = next_valid_thing(nonce, |bytes| {
Option::from(RandomSeed::from_bytes(bytes, &rho))
});
Self::new()
.default_recipient()
.value(NoteValue::from_raw(800_000))
.rho(rho)
.random_seed(rseed)
.clone()
}
}
impl Default for OrchardCryptoNoteBuilder {
fn default() -> Self {
Self::new()
.default_recipient()
.randomize_rho_and_rseed()
.value(NoteValue::from_raw(800_000))
.clone()
}
}
}
pub mod proposal {
use std::collections::BTreeMap;
use nonempty::NonEmpty;
use incrementalmerkletree::Position;
use pepper_sync::wallet::OutputId;
use sapling_crypto::Rseed;
use sapling_crypto::value::NoteValue;
use zcash_address::ZcashAddress;
use zcash_client_backend::fees::TransactionBalance;
use zcash_client_backend::proposal::{Proposal, ShieldedInputs, Step, StepOutput};
use zcash_client_backend::wallet::{ReceivedNote, WalletTransparentOutput};
use zcash_client_backend::zip321::{Payment, TransactionRequest};
use zcash_primitives::consensus::BlockHeight;
use zcash_primitives::transaction::fees::zip317::FeeRule;
use zcash_protocol::value::Zatoshis;
use zcash_protocol::{PoolType, ShieldedProtocol};
use super::{default_txid, default_zaddr};
use crate::utils::conversion::address_from_str;
use crate::utils::{build_method, build_method_push};
use crate::wallet::output::OutputRef;
pub struct ProposalBuilder {
fee_rule: Option<FeeRule>,
min_target_height: Option<BlockHeight>,
steps: Option<NonEmpty<Step<OutputRef>>>,
}
#[allow(dead_code)]
impl ProposalBuilder {
pub fn new() -> Self {
ProposalBuilder {
fee_rule: None,
min_target_height: None,
steps: None,
}
}
build_method!(fee_rule, FeeRule);
build_method!(min_target_height, BlockHeight);
build_method!(steps, NonEmpty<Step<OutputRef>>);
pub fn build(self) -> Proposal<FeeRule, OutputRef> {
let step = self.steps.unwrap().first().clone();
Proposal::single_step(
step.transaction_request().clone(),
step.payment_pools().clone(),
step.transparent_inputs().to_vec(),
step.shielded_inputs().cloned(),
step.balance().clone(),
self.fee_rule.unwrap(),
self.min_target_height.unwrap(),
step.is_shielding(),
)
.unwrap()
}
}
impl Default for ProposalBuilder {
fn default() -> Self {
let mut builder = ProposalBuilder::new();
builder
.fee_rule(FeeRule::standard())
.min_target_height(BlockHeight::from_u32(1))
.steps(NonEmpty::singleton(StepBuilder::default().build()));
builder
}
}
pub struct StepBuilder {
transaction_request: Option<TransactionRequest>,
payment_pools: Option<BTreeMap<usize, PoolType>>,
transparent_inputs: Option<Vec<WalletTransparentOutput>>,
shielded_inputs: Option<Option<ShieldedInputs<OutputRef>>>,
prior_step_inputs: Option<Vec<StepOutput>>,
balance: Option<TransactionBalance>,
is_shielding: Option<bool>,
}
impl StepBuilder {
pub fn new() -> Self {
StepBuilder {
transaction_request: None,
payment_pools: None,
transparent_inputs: None,
shielded_inputs: None,
prior_step_inputs: None,
balance: None,
is_shielding: None,
}
}
build_method!(transaction_request, TransactionRequest);
build_method!(payment_pools, BTreeMap<usize, PoolType>
);
build_method!(transparent_inputs, Vec<WalletTransparentOutput>);
build_method!(shielded_inputs, Option<ShieldedInputs<OutputRef>>);
build_method!(prior_step_inputs, Vec<StepOutput>);
build_method!(balance, TransactionBalance);
build_method!(is_shielding, bool);
pub fn build(self) -> Step<OutputRef> {
Step::from_parts(
&[],
self.transaction_request.unwrap(),
self.payment_pools.unwrap(),
self.transparent_inputs.unwrap(),
self.shielded_inputs.unwrap(),
self.prior_step_inputs.unwrap(),
self.balance.unwrap(),
self.is_shielding.unwrap(),
)
.unwrap()
}
}
impl Default for StepBuilder {
fn default() -> Self {
let txid = default_txid();
let (_, _, address) = default_zaddr();
let note = sapling_crypto::Note::from_parts(
address,
NoteValue::from_raw(120_000),
Rseed::AfterZip212([7; 32]),
);
let mut payment_pools = BTreeMap::new();
payment_pools.insert(0, PoolType::Shielded(ShieldedProtocol::Orchard));
let mut builder = Self::new();
builder
.transaction_request(TransactionRequestBuilder::default().build())
.payment_pools(payment_pools)
.transparent_inputs(vec![])
.shielded_inputs(Some(ShieldedInputs::from_parts(
BlockHeight::from_u32(1),
NonEmpty::singleton(ReceivedNote::from_parts(
OutputRef::new(OutputId::new(txid, 0), PoolType::SAPLING),
txid,
0,
zcash_client_backend::wallet::Note::Sapling(note),
zip32::Scope::External,
Position::from(1),
)),
)))
.prior_step_inputs(vec![])
.balance(TransactionBalance::new(vec![], Zatoshis::const_from_u64(20_000)).unwrap())
.is_shielding(false);
builder
}
}
pub struct TransactionRequestBuilder {
payments: Vec<Payment>,
}
impl TransactionRequestBuilder {
pub fn new() -> Self {
TransactionRequestBuilder { payments: vec![] }
}
build_method_push!(payments, Payment);
pub fn build(self) -> TransactionRequest {
TransactionRequest::new(self.payments).unwrap()
}
}
impl Default for TransactionRequestBuilder {
fn default() -> Self {
let mut builder = Self::new();
builder.payments(PaymentBuilder::default().build());
builder
}
}
pub struct PaymentBuilder {
recipient_address: Option<ZcashAddress>,
amount: Option<Zatoshis>,
}
impl PaymentBuilder {
pub fn new() -> Self {
PaymentBuilder {
recipient_address: None,
amount: None,
}
}
build_method!(recipient_address, ZcashAddress);
build_method!(amount, Zatoshis);
pub fn build(&self) -> Payment {
Payment::without_memo(
self.recipient_address.clone().unwrap(),
self.amount.unwrap(),
)
}
}
impl Default for PaymentBuilder {
fn default() -> Self {
let mut builder = Self::new();
builder
.recipient_address(
address_from_str(testvectors::REG_O_ADDR_FROM_ABANDONART).unwrap(),
)
.amount(Zatoshis::from_u64(100_000).unwrap());
builder
}
}
}