Skip to content

Commit

Permalink
Move shielded transactions out of Transfer and into Tx.
Browse files Browse the repository at this point in the history
  • Loading branch information
Murisi Tarusenga committed Apr 10, 2023
1 parent 1f35ef4 commit c4a2c5b
Show file tree
Hide file tree
Showing 25 changed files with 147 additions and 116 deletions.
126 changes: 61 additions & 65 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,8 +666,8 @@ impl ShieldedContext {
}
// Update this unknown shielded context until it is level with self
while tx_ctx.last_txidx != self.last_txidx {
if let Some(((height, idx), (epoch, tx))) = tx_iter.next() {
tx_ctx.scan_tx(*height, *idx, *epoch, tx);
if let Some(((height, idx), (epoch, tx, stx))) = tx_iter.next() {
tx_ctx.scan_tx(*height, *idx, *epoch, tx, stx);
} else {
break;
}
Expand All @@ -684,8 +684,8 @@ impl ShieldedContext {
}
// Now that we possess the unspent notes corresponding to both old and
// new keys up until tx_pos, proceed to scan the new transactions.
for ((height, idx), (epoch, tx)) in &mut tx_iter {
self.scan_tx(*height, *idx, *epoch, tx);
for ((height, idx), (epoch, tx, stx)) in &mut tx_iter {
self.scan_tx(*height, *idx, *epoch, tx, stx);
}
}

Expand Down Expand Up @@ -722,7 +722,7 @@ impl ShieldedContext {
pub async fn fetch_shielded_transfers(
ledger_address: &TendermintAddress,
last_txidx: u64,
) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer)> {
) -> BTreeMap<(BlockHeight, TxIndex), (Epoch, Transfer, Transaction)> {
let client = HttpClient::new(ledger_address.clone()).unwrap();
// The address of the MASP account
let masp_addr = masp();
Expand All @@ -742,15 +742,15 @@ impl ShieldedContext {
.push(&(TX_KEY_PREFIX.to_owned() + &i.to_string()))
.expect("Cannot obtain a storage key");
// Obtain the current transaction
let (tx_epoch, tx_height, tx_index, current_tx) =
query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>(
let (tx_epoch, tx_height, tx_index, current_tx, current_stx) =
query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer, Transaction)>(
&client,
&current_tx_key,
)
.await
.unwrap();
// Collect the current transaction
shielded_txs.insert((tx_height, tx_index), (tx_epoch, current_tx));
shielded_txs.insert((tx_height, tx_index), (tx_epoch, current_tx, current_stx));
}
shielded_txs
}
Expand All @@ -769,13 +769,8 @@ impl ShieldedContext {
index: TxIndex,
epoch: Epoch,
tx: &Transfer,
shielded: &Transaction,
) {
// Ignore purely transparent transactions
let shielded = if let Some(shielded) = &tx.shielded {
shielded
} else {
return;
};
// For tracking the account changes caused by this Transaction
let mut transaction_delta = TransactionDelta::new();
// Listen for notes sent to our viewing keys
Expand Down Expand Up @@ -1195,18 +1190,15 @@ impl ShieldedContext {
.push(&(TX_KEY_PREFIX.to_owned() + &txidx.to_string()))
.expect("Cannot obtain a storage key");
// Obtain the pointed to transaction
let (tx_epoch, _tx_height, _tx_index, tx) =
query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer)>(
let (tx_epoch, _tx_height, _tx_index, tx, shielded) =
query_storage_value::<(Epoch, BlockHeight, TxIndex, Transfer, Transaction)>(
&client, &tx_key,
)
.await
.expect("Ill-formed epoch, transaction pair");
// Accumulate the combined output note value into this Amount
let mut val_acc = Amount::zero();
let tx = tx
.shielded
.expect("Pinned Transfers should have shielded part");
for so in &tx.shielded_outputs {
for so in &shielded.shielded_outputs {
// Let's try to see if our viewing key can decrypt current note
let decres = try_sapling_note_decryption::<TestNetwork>(
0,
Expand Down Expand Up @@ -1634,63 +1626,67 @@ pub async fn submit_transfer(mut ctx: Context, args: args::TxTransfer) {
let is_source_faucet =
rpc::is_faucet_account(&source, args.tx.ledger_address.clone()).await;

let shielded = {
let spending_key = parsed_args.source.spending_key();
let payment_address = parsed_args.target.payment_address();
// No shielded components are needed when neither source nor
// destination are shielded
if spending_key.is_none() && payment_address.is_none() {
None
} else {
// We want to fund our transaction solely from supplied spending
// key
let spending_key = spending_key.map(|x| x.into());
let spending_keys: Vec<_> = spending_key.into_iter().collect();
// Load the current shielded context given the spending key we
// possess
let _ = ctx.shielded.load();
ctx.shielded
.fetch(&args.tx.ledger_address, &spending_keys, &[])
.await;
// Save the update state so that future fetches can be
// short-circuited
let _ = ctx.shielded.save();
let stx_result =
gen_shielded_transfer(&mut ctx, &parsed_args, shielded_gas)
.await;
match stx_result {
Ok(stx) => stx.map(|x| x.0),
Err(builder::Error::ChangeIsNegative(_)) => {
eprintln!(
"The balance of the source {} is lower than the \
amount to be transferred and fees. Amount to \
transfer is {} {} and fees are {} {}.",
parsed_args.source,
args.amount,
parsed_args.token,
args.tx.fee_amount,
parsed_args.tx.fee_token,
);
safe_exit(1)
}
Err(err) => panic!("{}", err),
}
}
};
let tx_code = ctx.read_wasm(TX_TRANSFER_WASM);
let mut tx = Tx::new(TxType::Raw(RawHeader::default()));
let masp_tx = shielded.map(|shielded| tx.add_section(Section::MaspTx(shielded)));
let transfer = token::Transfer {
source,
target,
token,
sub_prefix,
amount,
key,
shielded: {
let spending_key = parsed_args.source.spending_key();
let payment_address = parsed_args.target.payment_address();
// No shielded components are needed when neither source nor
// destination are shielded
if spending_key.is_none() && payment_address.is_none() {
None
} else {
// We want to fund our transaction solely from supplied spending
// key
let spending_key = spending_key.map(|x| x.into());
let spending_keys: Vec<_> = spending_key.into_iter().collect();
// Load the current shielded context given the spending key we
// possess
let _ = ctx.shielded.load();
ctx.shielded
.fetch(&args.tx.ledger_address, &spending_keys, &[])
.await;
// Save the update state so that future fetches can be
// short-circuited
let _ = ctx.shielded.save();
let stx_result =
gen_shielded_transfer(&mut ctx, &parsed_args, shielded_gas)
.await;
match stx_result {
Ok(stx) => stx.map(|x| x.0),
Err(builder::Error::ChangeIsNegative(_)) => {
eprintln!(
"The balance of the source {} is lower than the \
amount to be transferred and fees. Amount to \
transfer is {} {} and fees are {} {}.",
parsed_args.source,
args.amount,
parsed_args.token,
args.tx.fee_amount,
parsed_args.tx.fee_token,
);
safe_exit(1)
}
Err(err) => panic!("{}", err),
}
}
},
shielded: masp_tx.map(
|masp_tx| Hash(masp_tx.hash(&mut Sha256::new()).finalize_reset().into())
),
};
tracing::debug!("Transfer data {:?}", transfer);
let data = transfer
.try_to_vec()
.expect("Encoding tx data shouldn't fail");
let tx_code = ctx.read_wasm(TX_TRANSFER_WASM);
let mut tx = Tx::new(TxType::Raw(RawHeader::default()));
tx.set_data(Data::new(data));
tx.set_code(Code::new(tx_code));
let signing_address = TxSigningKey::WalletAddress(args.source.to_address());
Expand Down
15 changes: 15 additions & 0 deletions core/src/proto/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::types::transaction::TxType;
use crate::types::transaction::WrapperTx;
use sha2::{Digest, Sha256};
use crate::types::transaction::WrapperTxErr;
use masp_primitives::transaction::Transaction;

#[derive(Error, Debug)]
pub enum Error {
Expand Down Expand Up @@ -353,6 +354,7 @@ pub enum Section {
Code(Code),
Signature(Signature),
Ciphertext(Ciphertext),
MaspTx(Transaction),
}

impl Section {
Expand All @@ -378,6 +380,11 @@ impl Section {
hasher.update(&[4]);
ct.hash(hasher)
}
Self::MaspTx(tx) => {
hasher.update(&[5]);
hasher.update(tx.try_to_vec().expect("unable to serialize tx"));
hasher
},
}
}

Expand Down Expand Up @@ -426,6 +433,14 @@ impl Section {
None
}
}

pub fn masp_tx(&self) -> Option<Transaction> {
if let Self::MaspTx(data) = self {
Some(data.clone())
} else {
None
}
}
}

/// A SigningTx but with the full code embedded. This structure will almost
Expand Down
1 change: 1 addition & 0 deletions core/src/types/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub type HashResult<T> = std::result::Result<T, Error>;
Debug,
Default,
Hash,
PartialOrd,
PartialEq,
Eq,
BorshSerialize,
Expand Down
3 changes: 2 additions & 1 deletion core/src/types/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use thiserror::Error;

use crate::types::address::{masp, Address, DecodeError as AddressError};
use crate::types::storage::{DbKeySeg, Key, KeySeg};
use crate::types::hash::Hash;

/// Amount in micro units. For different granularity another representation
/// might be more appropriate.
Expand Down Expand Up @@ -415,7 +416,7 @@ pub struct Transfer {
/// The unused storage location at which to place TxId
pub key: Option<String>,
/// Shielded transaction part
pub shielded: Option<Transaction>,
pub shielded: Option<Hash>,
}

#[allow(missing_docs)]
Expand Down
15 changes: 6 additions & 9 deletions shared/src/vm/host_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1749,7 +1749,7 @@ where
EVAL: VpEvaluator,
CA: WasmCacheAccess,
{
use crate::types::token::Transfer;
use masp_primitives::transaction::Transaction;

let gas_meter = unsafe { env.ctx.gas_meter.get() };
let (tx_bytes, gas) = env
Expand All @@ -1758,17 +1758,14 @@ where
.map_err(|e| vp_host_fns::RuntimeError::MemoryError(Box::new(e)))?;
vp_host_fns::add_gas(gas_meter, gas)?;

let full_tx: Transfer =
let shielded: Transaction =
BorshDeserialize::try_from_slice(tx_bytes.as_slice())
.map_err(vp_host_fns::RuntimeError::EncodingError)?;

match full_tx.shielded {
Some(shielded_tx) => Ok(HostEnvResult::from(
crate::ledger::masp::verify_shielded_tx(&shielded_tx),
)
.to_i64()),
None => Ok(HostEnvResult::Fail.to_i64()),
}
Ok(HostEnvResult::from(
crate::ledger::masp::verify_shielded_tx(&shielded),
)
.to_i64())
}

/// Log a string from exposed to the wasm VM Tx environment. The message will be
Expand Down
1 change: 1 addition & 0 deletions tx_prelude/src/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub fn init_proposal(ctx: &mut Ctx, data: InitProposalData) -> TxResult {
min_proposal_funds,
&None,
&None,
&None,
)
}

Expand Down
2 changes: 1 addition & 1 deletion tx_prelude/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use namada_core::ledger::storage_api::{
StorageRead, StorageWrite,
};
pub use namada_core::ledger::tx_env::TxEnv;
pub use namada_core::proto::{Signed, Tx};
pub use namada_core::proto::{Signed, Tx, Section};
pub use namada_core::types::address::Address;
use namada_core::types::chain::CHAIN_ID_LENGTH;
use namada_core::types::internal::HostEnvResult;
Expand Down
2 changes: 1 addition & 1 deletion tx_prelude/src/proof_of_stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ impl namada_proof_of_stake::PosActions for Ctx {
dest: &Address,
) -> storage_api::Result<()> {
crate::token::transfer(
self, src, dest, token, None, amount, &None, &None,
self, src, dest, token, None, amount, &None, &None, &None,
)
}
}
18 changes: 11 additions & 7 deletions tx_prelude/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use namada_core::types::address::{Address, InternalAddress};
use namada_core::types::storage::KeySeg;
use namada_core::types::token;
pub use namada_core::types::token::*;
use namada_core::types::hash::Hash;

use super::*;

Expand All @@ -16,6 +17,7 @@ pub fn transfer(
sub_prefix: Option<storage::Key>,
amount: Amount,
key: &Option<String>,
shielded_hash: &Option<Hash>,
shielded: &Option<Transaction>,
) -> TxResult {
if amount != Amount::default() {
Expand Down Expand Up @@ -108,16 +110,18 @@ pub fn transfer(
sub_prefix: None,
amount,
key: key.clone(),
shielded: Some(shielded.clone()),
shielded: shielded_hash.clone(),
};
let record: (Epoch, BlockHeight, TxIndex, Transfer, Transaction) = (
ctx.get_block_epoch()?,
ctx.get_block_height()?,
ctx.get_tx_index()?,
transfer,
shielded.clone(),
);
ctx.write(
&current_tx_key,
(
ctx.get_block_epoch()?,
ctx.get_block_height()?,
ctx.get_tx_index()?,
transfer,
),
record,
)?;
ctx.write(&head_tx_key, current_tx_idx + 1)?;
// If storage key has been supplied, then pin this transaction to it
Expand Down
Loading

0 comments on commit c4a2c5b

Please sign in to comment.