Audit finding: Total supply of native assets can exceed the set limit
Project: MantaNetwork
Auditor: Veridise
Location: pallets/asset-manager/src/lib.rs
Impact: Medium
Vulnerability class (audit): Logic Error
Vulnerability class (Scout): Business Logic
Status: Acknowledged

4.1.4 V-MANC-VUL-004: Total supply of native assets can exceed the set limit

Severity: Medium
Type: Logic Error
Commit: 45ba60e1d
Status: Acknowledged

File(s): pallets/asset-manager/src/lib.rs
Location(s): mint_asset

One invariant underlying the correctness of MantaPay is that the total supply of an asset cannot exceed the maximum amount that can be held in a particular account. This is because MantaPay uses a dedicated account A to store the value of all the private assets. As such, A should, in principle, be able to hold all the supply in the case where all of that asset is privatized.

In more detail, when privatizing a user’s public assets (via to_private), MantaPay constructs opaque UTXOs to encode the amount privatized, and then transfers those public assets into A. This transfer is expected not to fail because of the invariant described above. However, we found a case where the transfer can fail.

Manta enforces this invariant for NonNative assets because every time an asset is minted into an account, the total supply is increased. If the total supply would exceed the maximum that can be held in an account, the mint fails with the error Overflow. However, there is no such check for Native assets. Thus, if the total supply of Native assets exceeds the maximum that can be held in an account, u128::MAX, then to_private calls that should succeed can fail if the amount held in A is close to the maximum allowed. This is demonstrated in Snippet 4.2.

Impact: By not constraining the amount of Native assets to be less than the maximum amount that can be held in an account, to_private transactions that should succeed will fail.

Recommendation: We recommend a similar check be done for Native assets as is done for NonNative assets to enforce that the total supply cannot exceed the maximum that can be held in a given account.

Developer Response: The developers have acknowledged the issue and are determining how to address it.

Snippet 4.2: Failed to_private due to count of native assets exceeding u128::MAX

#[test]
fn public_account_issue() {
let mut rng = OsRng;
new_test_ext().execute_with(|| {
let asset_id = NATIVE_ASSET_ID;
let value = 1000u128;
let id = NATIVE_ASSET_ID;
let metadata = AssetRegistryMetadata {
metadata: AssetStorageMetadata {
name: b"Calamari".to_vec(),
symbol: b"KMA".to_vec(),
decimals: 12,
is_frozen: false,
},
min_balance: TEST_DEFAULT_ASSET_ED2,
is_sufficient: true,
};
assert_ok!(MantaAssetRegistry::create_asset(
id, metadata.into(), TEST_DEFAULT_ASSET_ED2,
true
));
assert_ok!(FungibleLedger::<Test>::deposit_minting(id, &ALICE, 2*value));
assert_ok!(FungibleLedger::<Test>::deposit_minting(id, &MantaPay::account_id(), u128::MAX));
let mut utxo_accumulator = UtxoAccumulator::new(UTXO_ACCUMULATOR_MODEL.clone());
let spending_key = rng.gen();
let address = PARAMETERS.address_from_spending_key(&spending_key);
let mut authorization =
Authorization::from_spending_key(&PARAMETERS, &spending_key, &mut rng);
let asset_0 = Asset::new(Fp::from(asset_id), value);
let (to_private_0, pre_sender_0) = ToPrivate::internal_pair(
&PARAMETERS, &mut authorization.context,
address, asset_0,
Default::default(), &mut rng,
);
let to_private_0 = to_private_0
.into_post(
FullParametersRef::new(&PARAMETERS, utxo_accumulator.model()),
&PROVING_CONTEXT.to_private,
None, Vec::new(), &mut rng,
)
.expect("Unable to build TO_PRIVATE proof.")
.expect("Did not match transfer shape.");
assert_ok!(MantaPay::to_private(
MockOrigin::signed(ALICE),
PalletTransferPost::try_from(to_private_0).unwrap()
));
}
}


Summary: Native asset supply can exceed the maximum limit for an account, causing to_private transactions to fail.