Skip to content

Commit

Permalink
Merge branch 'murisi/masp-test-vectors-rebased' (#2427)
Browse files Browse the repository at this point in the history
* origin/murisi/masp-test-vectors-rebased:
  Added changelog entry.
  Reified the pre-image of AssetTypes.
  Hashed the token denomination into AssetTypes to tighten HW wallet signing.
  Constrained the range of MASP test vectors generated for correctness.
  Now generating binding test vectors binding signatures correctly.
  Increased usage of prop_oneof macro.
  Now generating arbitrary ShieldedTransfer.
  • Loading branch information
tzemanovic committed Jan 25, 2024
2 parents 8bf2360 + 3a3cd92 commit 51eb113
Show file tree
Hide file tree
Showing 64 changed files with 1,342 additions and 313 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- The test vector generator now supports generating MASP transactions.
([\#2427](https://github.com/anoma/namada/pull/2427))
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ bech32 = "0.8.0"
bimap = {version = "0.6.2", features = ["serde"]}
bit-set = "0.5.2"
blake2b-rs = "0.2.0"
bls12_381 = "0.8"
byte-unit = "4.0.13"
byteorder = "1.4.2"
borsh = {version = "1.2.0", features = ["unstable__schema", "derive"]}
Expand Down Expand Up @@ -105,6 +106,7 @@ ibc-testkit = {version = "0.48.1", default-features = false}
ics23 = "0.11.0"
index-set = { git = "https://github.com/heliaxdev/index-set", tag = "v0.8.1", features = ["serialize-borsh", "serialize-serde"] }
itertools = "0.10.0"
jubjub = "0.10"
k256 = { version = "0.13.0", default-features = false, features = ["ecdsa", "pkcs8", "precomputed-tables", "serde", "std"]}
lazy_static = "1.4.0"
ledger-namada-rs = { git = "https://github.com/Zondax/ledger-namada", tag = "v0.0.12" }
Expand Down
25 changes: 16 additions & 9 deletions crates/apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress};
use namada::types::storage::{
BlockHeight, BlockResults, Epoch, IndexedTx, Key, KeySeg,
};
use namada::types::token::{Change, MaspDenom};
use namada::types::token::{Change, MaspDigitPos};
use namada::{state as storage, token};
use namada_sdk::error::{
is_pinned_error, Error, PinnedBalanceError, QueryError,
Expand Down Expand Up @@ -132,7 +132,7 @@ pub async fn query_transfers(
let mut shielded = context.shielded_mut().await;
let _ = shielded.load().await;
// Precompute asset types to increase chances of success in decoding
let _ = shielded.precompute_asset_types(&wallet);
let _ = shielded.precompute_asset_types(context).await;
// Obtain the effects of all shielded and transparent transactions
let transfers = shielded
.query_tx_deltas(
Expand Down Expand Up @@ -431,7 +431,11 @@ pub async fn query_pinned_balance(
.collect();
let _ = context.shielded_mut().await.load().await;
// Precompute asset types to increase chances of success in decoding
let _ = context.shielded_mut().await.precompute_asset_types(&wallet);
let _ = context
.shielded_mut()
.await
.precompute_asset_types(context)
.await;
// Print the token balances by payment address
for owner in owners {
let mut balance =
Expand Down Expand Up @@ -880,7 +884,7 @@ pub async fn query_shielded_balance(
.collect();
shielded.fetch(context.client(), &[], &fvks).await.unwrap();
// Precompute asset types to increase chances of success in decoding
let _ = shielded.precompute_asset_types(&*context.wallet().await);
let _ = shielded.precompute_asset_types(context).await;
// Save the update state so that future fetches can be short-circuited
let _ = shielded.save().await;
}
Expand Down Expand Up @@ -2444,7 +2448,7 @@ pub async fn query_conversions(
.expect("Conversions should be defined");
// Track whether any non-sentinel conversions are found
let mut conversions_found = false;
for (addr, epoch, amt) in conversions.values() {
for (addr, _denom, digit, epoch, amt) in conversions.values() {
// If the user has specified any targets, then meet them
// If we have a sentinel conversion, then skip printing
if matches!(&target_token, Some(target) if target != addr)
Expand All @@ -2457,23 +2461,25 @@ pub async fn query_conversions(
// Print the asset to which the conversion applies
display!(
context.io(),
"{}[{}]: ",
"{}*2^{}[{}]: ",
tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()),
*digit as u8 * 64,
epoch,
);
// Now print out the components of the allowed conversion
let mut prefix = "";
for (asset_type, val) in amt.components() {
// Look up the address and epoch of asset to facilitate pretty
// printing
let (addr, epoch, _) = &conversions[asset_type];
let (addr, _denom, digit, epoch, _) = &conversions[asset_type];
// Now print out this component of the conversion
display!(
context.io(),
"{}{} {}[{}]",
"{}{} {}*2^{}[{}]",
prefix,
val,
tokens.get(addr).cloned().unwrap_or_else(|| addr.clone()),
*digit as u8 * 64,
epoch
);
// Future iterations need to be prefixed with +
Expand All @@ -2496,7 +2502,8 @@ pub async fn query_conversion<C: namada::ledger::queries::Client + Sync>(
asset_type: AssetType,
) -> Option<(
Address,
MaspDenom,
token::Denomination,
MaspDigitPos,
Epoch,
masp_primitives::transaction::components::I128Sum,
MerklePath<Node>,
Expand Down
17 changes: 8 additions & 9 deletions crates/core/src/types/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,15 +499,14 @@ pub mod testing {
arb_keypair::<S>().prop_map(|x| x.ref_to())
}

/// Generate an arbitrary common key
pub fn arb_common_pk() -> impl Strategy<Value = common::PublicKey> {
let ed25519 = arb_pk::<ed25519::SigScheme>()
.prop_map(common::PublicKey::Ed25519)
.sboxed();
let secp256k1 = arb_pk::<secp256k1::SigScheme>()
.prop_map(common::PublicKey::Secp256k1)
.sboxed();
ed25519.prop_union(secp256k1)
prop_compose! {
/// Generate an arbitrary common key
pub fn arb_common_pk()(pk in prop_oneof![
arb_pk::<ed25519::SigScheme>().prop_map(common::PublicKey::Ed25519),
arb_pk::<secp256k1::SigScheme>().prop_map(common::PublicKey::Secp256k1),
]) -> common::PublicKey {
pk
}
}

/// A keypair for tests
Expand Down
79 changes: 67 additions & 12 deletions crates/core/src/types/masp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::str::FromStr;
use borsh::{BorshDeserialize, BorshSerialize};
use borsh_ext::BorshSerializeExt;
use masp_primitives::asset_type::AssetType;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::impl_display_and_from_str_via_format;
Expand All @@ -15,23 +16,77 @@ use crate::types::string_encoding::{
self, MASP_EXT_FULL_VIEWING_KEY_HRP, MASP_EXT_SPENDING_KEY_HRP,
MASP_PAYMENT_ADDRESS_HRP,
};
use crate::types::token::MaspDenom;
use crate::types::token::{Denomination, MaspDigitPos};

/// The plain representation of a MASP aaset
#[derive(
BorshSerialize,
BorshDeserialize,
Clone,
Debug,
PartialOrd,
Ord,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
)]
pub struct AssetData {
/// The token associated with this asset type
pub token: Address,
/// The denomination associated with the above toke
pub denom: Denomination,
/// The digit position covered by this asset type
pub position: MaspDigitPos,
/// The epoch of the asset type, if any
pub epoch: Option<Epoch>,
}

impl AssetData {
/// Make asset type corresponding to given address and epoch
pub fn encode(&self) -> Result<AssetType, std::io::Error> {
// Timestamp the chosen token with the current epoch
let token_bytes = self.serialize_to_vec();
// Generate the unique asset identifier from the unique token address
AssetType::new(token_bytes.as_ref()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"unable to create asset type".to_string(),
)
})
}

/// Give this pre-asset type the given epoch if already has an epoch. Return
/// the replaced value.
pub fn redate(&mut self, to: Epoch) -> Option<Epoch> {
if self.epoch.is_some() {
self.epoch.replace(to)
} else {
None
}
}

/// Remove the epoch associated with this pre-asset type
pub fn undate(&mut self) {
self.epoch = None;
}
}

/// Make asset type corresponding to given address and epoch
pub fn encode_asset_type(
token: Address,
denom: Denomination,
position: MaspDigitPos,
epoch: Option<Epoch>,
token: &Address,
denom: MaspDenom,
) -> Result<AssetType, std::io::Error> {
// Timestamp the chosen token with the current epoch
let token_bytes = (token, denom, epoch).serialize_to_vec();
// Generate the unique asset identifier from the unique token address
AssetType::new(token_bytes.as_ref()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"unable to create asset type".to_string(),
)
})
AssetData {
token,
denom,
position,
epoch,
}
.encode()
}

// enough capacity to store the payment address
Expand Down
25 changes: 15 additions & 10 deletions crates/core/src/types/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ pub struct ConversionState {
#[allow(clippy::type_complexity)]
pub assets: BTreeMap<
AssetType,
((Address, MaspDenom), Epoch, AllowedConversion, usize),
(
(Address, Denomination, MaspDigitPos),
Epoch,
AllowedConversion,
usize,
),
>,
}

Expand Down Expand Up @@ -225,19 +230,19 @@ impl Amount {
}
}

/// Given a u64 and [`MaspDenom`], construct the corresponding
/// Given a u64 and [`MaspDigitPos`], construct the corresponding
/// amount.
pub fn from_masp_denominated(val: u64, denom: MaspDenom) -> Self {
pub fn from_masp_denominated(val: u64, denom: MaspDigitPos) -> Self {
let mut raw = [0u64; 4];
raw[denom as usize] = val;
Self { raw: Uint(raw) }
}

/// Given a u128 and [`MaspDenom`], construct the corresponding
/// Given a u128 and [`MaspDigitPos`], construct the corresponding
/// amount.
pub fn from_masp_denominated_u128(
val: u128,
denom: MaspDenom,
denom: MaspDigitPos,
) -> Option<Self> {
let lo = val as u64;
let hi = (val >> 64) as u64;
Expand Down Expand Up @@ -927,14 +932,14 @@ impl From<Amount> for Uint {
#[repr(u8)]
#[allow(missing_docs)]
#[borsh(use_discriminant = true)]
pub enum MaspDenom {
pub enum MaspDigitPos {
Zero = 0,
One,
Two,
Three,
}

impl From<u8> for MaspDenom {
impl From<u8> for MaspDigitPos {
fn from(denom: u8) -> Self {
match denom {
0 => Self::Zero,
Expand All @@ -946,9 +951,9 @@ impl From<u8> for MaspDenom {
}
}

impl MaspDenom {
impl MaspDigitPos {
/// Iterator over the possible denominations
pub fn iter() -> impl Iterator<Item = MaspDenom> {
pub fn iter() -> impl Iterator<Item = MaspDigitPos> {
// 0, 1, 2, 3
(0u8..4).map(Self::from)
}
Expand Down Expand Up @@ -1275,7 +1280,7 @@ mod tests {
fn test_from_masp_denominated() {
let uint = Uint([15u64, 16, 17, 18]);
let original = Amount::from_uint(uint, 0).expect("Test failed");
for denom in MaspDenom::iter() {
for denom in MaspDigitPos::iter() {
let word = denom.denominate(&original);
assert_eq!(word, denom as u64 + 15u64);
let amount = Amount::from_masp_denominated(word, denom);
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/types/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use uint::construct_uint;

use super::dec::{Dec, POS_DECIMAL_PRECISION};
use crate::types::token;
use crate::types::token::{Amount, AmountParseError, MaspDenom};
use crate::types::token::{Amount, AmountParseError, MaspDigitPos};

/// The value zero.
pub const ZERO: Uint = Uint::from_u64(0);
Expand Down Expand Up @@ -569,7 +569,7 @@ impl I256 {
/// using the given denomination.
pub fn from_masp_denominated(
value: impl Into<i128>,
denom: MaspDenom,
denom: MaspDigitPos,
) -> Result<Self, AmountParseError> {
let value = value.into();
let is_negative = value < 0;
Expand Down
Loading

0 comments on commit 51eb113

Please sign in to comment.