Skip to content

Commit

Permalink
Fix InvalidTransactionNativeTokensCount error for multiple entries wi…
Browse files Browse the repository at this point in the history
…th the same native token
  • Loading branch information
Thoralf-M committed Dec 6, 2023
1 parent 05394f8 commit c06a05a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
1 change: 1 addition & 0 deletions sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- `needs_blind_signing()` for non Ed25519 addresses;
- InvalidTransactionNativeTokensCount error for multiple entries with the same native token;

## 1.1.2 - 2023-10-26

Expand Down
14 changes: 7 additions & 7 deletions sdk/src/types/block/payload/transaction/essence/regular.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use packable::{bounded::BoundedU16, prefix::BoxedSlicePrefix, Packable};
use crate::types::{
block::{
input::{Input, INPUT_COUNT_RANGE},
output::{InputsCommitment, NativeTokens, Output, OUTPUT_COUNT_RANGE},
output::{InputsCommitment, NativeTokens, Output, TokenId, OUTPUT_COUNT_RANGE},
payload::{OptionalPayload, Payload},
protocol::ProtocolParameters,
Error,
Expand Down Expand Up @@ -219,7 +219,7 @@ fn verify_inputs_packable<const VERIFY: bool>(inputs: &[Input], _visitor: &Proto
fn verify_outputs<const VERIFY: bool>(outputs: &[Output], visitor: &ProtocolParameters) -> Result<(), Error> {
if VERIFY {
let mut amount_sum: u64 = 0;
let mut native_tokens_count: u8 = 0;
let mut total_native_tokens: HashSet<TokenId> = HashSet::new();
let mut chain_ids = HashSet::new();

for output in outputs.iter() {
Expand All @@ -240,12 +240,12 @@ fn verify_outputs<const VERIFY: bool>(outputs: &[Output], visitor: &ProtocolPara
return Err(Error::InvalidTransactionAmountSum(amount_sum as u128));
}

native_tokens_count = native_tokens_count.checked_add(native_tokens.len() as u8).ok_or(
Error::InvalidTransactionNativeTokensCount(native_tokens_count as u16 + native_tokens.len() as u16),
)?;
total_native_tokens.extend(native_tokens.iter().map(|n| n.token_id()));

if native_tokens_count > NativeTokens::COUNT_MAX {
return Err(Error::InvalidTransactionNativeTokensCount(native_tokens_count as u16));
if total_native_tokens.len() > NativeTokens::COUNT_MAX.into() {
return Err(Error::InvalidTransactionNativeTokensCount(
total_native_tokens.len() as u16
));
}

if let Some(chain_id) = chain_id {
Expand Down
60 changes: 60 additions & 0 deletions sdk/tests/types/transaction_regular_essence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,63 @@ fn duplicate_output_foundry() {
Err(Error::DuplicateOutputChain(ChainId::Foundry(foundry_id_0))) if foundry_id_0 == foundry_id
));
}

#[test]
fn more_than_64_same_native_tokens() {
let protocol_parameters = protocol_parameters();
let transaction_id = TransactionId::new(prefix_hex::decode(TRANSACTION_ID).unwrap());
let input = Input::Utxo(UtxoInput::new(transaction_id, 0).unwrap());
let bytes: [u8; 32] = prefix_hex::decode(ED25519_ADDRESS_1).unwrap();
let address = Address::from(Ed25519Address::new(bytes));
let amount = 1_000_000;
let alias_id = AliasId::from(bytes);
let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new(70, 0, 100).unwrap());
let foundry_id = FoundryId::build(&AliasAddress::from(alias_id), 1, token_scheme.kind());
let token_id = TokenId::from(foundry_id);
let basic = BasicOutput::build_with_amount(amount)
.add_native_token(NativeToken::new(token_id, 70).unwrap())
.add_unlock_condition(AddressUnlockCondition::new(address))
.finish_output(protocol_parameters.token_supply())
.unwrap();

let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment())
.with_inputs([input])
.with_outputs(vec![basic; 100])
.finish_with_params(&protocol_parameters);

assert!(essence.is_ok());
}

#[test]
fn more_than_64_distinctive_native_tokens() {
let protocol_parameters = protocol_parameters();
let transaction_id = TransactionId::new(prefix_hex::decode(TRANSACTION_ID).unwrap());
let input = Input::Utxo(UtxoInput::new(transaction_id, 0).unwrap());
let bytes: [u8; 32] = prefix_hex::decode(ED25519_ADDRESS_1).unwrap();
let address = Address::from(Ed25519Address::new(bytes));
let amount = 1_000_000;

let mut outputs = Vec::new();
for _ in 0..65 {
let alias_id = AliasId::from(rand_bytes_array());
let token_scheme = TokenScheme::Simple(SimpleTokenScheme::new(70, 0, 100).unwrap());
let foundry_id = FoundryId::build(&AliasAddress::from(alias_id), 1, token_scheme.kind());
let token_id = TokenId::from(foundry_id);
let basic = BasicOutput::build_with_amount(amount)
.add_native_token(NativeToken::new(token_id, 70).unwrap())
.add_unlock_condition(AddressUnlockCondition::new(address))
.finish_output(protocol_parameters.token_supply())
.unwrap();
outputs.push(basic);
}

let essence = RegularTransactionEssence::builder(protocol_parameters.network_id(), rand_inputs_commitment())
.with_inputs([input])
.with_outputs(outputs)
.finish_with_params(&protocol_parameters);

assert!(matches!(
essence,
Err(Error::InvalidTransactionNativeTokensCount(count)) if count == 65
));
}

0 comments on commit c06a05a

Please sign in to comment.