Skip to content

Commit 10fba62

Browse files
committed
Initial tests
1 parent a6eaf4f commit 10fba62

File tree

8 files changed

+370
-18
lines changed

8 files changed

+370
-18
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// Copyright (c) 2022 RBB S.r.l
2+
3+
// SPDX-License-Identifier: MIT
4+
// Licensed under the MIT License;
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://github.com/mintlayer/mintlayer-core/blob/master/LICENSE
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
use std::vec;
17+
18+
use chainstate::{
19+
BlockError, BlockSource, ChainstateError, CheckBlockError, CheckBlockTransactionsError,
20+
TokensError,
21+
};
22+
use chainstate_test_framework::{get_output_value, TestFramework, TransactionBuilder};
23+
use common::chain::tokens::{TokenIssuanceV1, TokenTotalSupply};
24+
use common::{
25+
chain::{
26+
output_value::OutputValue, signature::inputsig::InputWitness, Destination,
27+
OutPointSourceId, TxInput, TxOutput,
28+
},
29+
primitives::Idable,
30+
};
31+
use crypto::random::Rng;
32+
use rstest::rstest;
33+
use test_utils::{
34+
gen_text_with_non_ascii,
35+
random::{make_seedable_rng, Seed},
36+
random_string,
37+
};
38+
use tx_verifier::error::TokenIssuanceError;
39+
40+
#[rstest]
41+
#[trace]
42+
#[case(Seed::from_entropy())]
43+
fn token_issue_test(#[case] seed: Seed) {
44+
utils::concurrency::model(move || {
45+
let mut rng = make_seedable_rng(seed);
46+
let mut tf = TestFramework::builder(&mut rng).build();
47+
let outpoint_source_id: OutPointSourceId = tf.genesis().get_id().into();
48+
49+
let token_min_issuance_fee = tf.chainstate.get_chain_config().token_min_issuance_fee();
50+
let token_max_ticker_len = tf.chainstate.get_chain_config().token_max_ticker_len();
51+
let token_max_dec_count = tf.chainstate.get_chain_config().token_max_dec_count();
52+
let token_max_uri_len = tf.chainstate.get_chain_config().token_max_uri_len();
53+
54+
let mut process_block_with_issuance = |issuance: TokenIssuanceV1| {
55+
let tx = TransactionBuilder::new()
56+
.add_input(
57+
TxInput::from_utxo(outpoint_source_id.clone(), 0),
58+
InputWitness::NoSignature(None),
59+
)
60+
.add_output(TxOutput::Transfer(
61+
issuance.into(),
62+
Destination::AnyoneCanSpend,
63+
))
64+
.add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
65+
.build();
66+
let tx_id = tx.transaction().get_id();
67+
let block = tf.make_block_builder().add_transaction(tx).build();
68+
let block_id = block.get_id();
69+
let result = tf.process_block(block, BlockSource::Local);
70+
(result, tx_id, block_id)
71+
};
72+
73+
// Ticker is too long
74+
let issuance = TokenIssuanceV1 {
75+
token_ticker: random_string(&mut rng, 10..u16::MAX as usize).as_bytes().to_vec(),
76+
number_of_decimals: rng.gen_range(1..18),
77+
metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
78+
supply_limit: TokenTotalSupply::Unlimited,
79+
reissuance_controller: Destination::AnyoneCanSpend,
80+
};
81+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
82+
assert_eq!(
83+
result,
84+
Err(ChainstateError::ProcessBlockError(
85+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
86+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
87+
TokenIssuanceError::IssueErrorInvalidTickerLength,
88+
tx_id,
89+
block_id
90+
))
91+
))
92+
))
93+
);
94+
95+
// Ticker doesn't exist
96+
let issuance = TokenIssuanceV1 {
97+
token_ticker: b"".to_vec(),
98+
number_of_decimals: rng.gen_range(1..18),
99+
metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
100+
supply_limit: TokenTotalSupply::Unlimited,
101+
reissuance_controller: Destination::AnyoneCanSpend,
102+
};
103+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
104+
assert_eq!(
105+
result,
106+
Err(ChainstateError::ProcessBlockError(
107+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
108+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
109+
TokenIssuanceError::IssueErrorInvalidTickerLength,
110+
tx_id,
111+
block_id
112+
))
113+
))
114+
))
115+
);
116+
117+
{
118+
// try all possible chars for ticker and ensure everything fails except for alphanumeric chars
119+
for c in u8::MIN..u8::MAX {
120+
// if c is alphanumeric, then this doesn't produce an error, skip it
121+
if c.is_ascii_alphanumeric() {
122+
continue;
123+
}
124+
125+
let token_ticker = gen_text_with_non_ascii(c, &mut rng, token_max_ticker_len);
126+
127+
// Ticker contain non alpha-numeric char
128+
let issuance = TokenIssuanceV1 {
129+
token_ticker,
130+
number_of_decimals: rng.gen_range(1..18),
131+
metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
132+
supply_limit: TokenTotalSupply::Unlimited,
133+
reissuance_controller: Destination::AnyoneCanSpend,
134+
};
135+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
136+
137+
assert_eq!(
138+
result,
139+
Err(ChainstateError::ProcessBlockError(
140+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
141+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
142+
TokenIssuanceError::IssueErrorTickerHasNoneAlphaNumericChar,
143+
tx_id,
144+
block_id
145+
))
146+
))
147+
))
148+
);
149+
}
150+
}
151+
152+
// Too many decimals
153+
{
154+
let decimals_count_to_use = token_max_dec_count + 1;
155+
156+
let issuance = TokenIssuanceV1 {
157+
token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
158+
number_of_decimals: decimals_count_to_use,
159+
metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
160+
supply_limit: TokenTotalSupply::Unlimited,
161+
reissuance_controller: Destination::AnyoneCanSpend,
162+
};
163+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
164+
assert_eq!(
165+
result,
166+
Err(ChainstateError::ProcessBlockError(
167+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
168+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
169+
TokenIssuanceError::IssueErrorTooManyDecimals,
170+
tx_id,
171+
block_id
172+
))
173+
))
174+
))
175+
);
176+
}
177+
178+
// URI is too long
179+
{
180+
let uri_len_range_to_use = (token_max_uri_len + 1)..u16::MAX as usize;
181+
182+
let issuance = TokenIssuanceV1 {
183+
token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
184+
number_of_decimals: rng.gen_range(1..18),
185+
metadata_uri: random_string(&mut rng, uri_len_range_to_use).as_bytes().to_vec(),
186+
supply_limit: TokenTotalSupply::Unlimited,
187+
reissuance_controller: Destination::AnyoneCanSpend,
188+
};
189+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
190+
assert_eq!(
191+
result,
192+
Err(ChainstateError::ProcessBlockError(
193+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
194+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
195+
TokenIssuanceError::IssueErrorIncorrectMetadataURI,
196+
tx_id,
197+
block_id
198+
))
199+
))
200+
))
201+
);
202+
}
203+
204+
// URI contain non alpha-numeric char
205+
let issuance = TokenIssuanceV1 {
206+
token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
207+
number_of_decimals: rng.gen_range(1..18),
208+
metadata_uri: "https://💖🚁🌭.🦠🚀🚖🚧".as_bytes().to_vec(),
209+
supply_limit: TokenTotalSupply::Unlimited,
210+
reissuance_controller: Destination::AnyoneCanSpend,
211+
};
212+
let (result, tx_id, block_id) = process_block_with_issuance(issuance);
213+
assert_eq!(
214+
result,
215+
Err(ChainstateError::ProcessBlockError(
216+
BlockError::CheckBlockFailed(CheckBlockError::CheckTransactionFailed(
217+
CheckBlockTransactionsError::TokensError(TokensError::IssueError(
218+
TokenIssuanceError::IssueErrorIncorrectMetadataURI,
219+
tx_id,
220+
block_id
221+
))
222+
))
223+
))
224+
);
225+
226+
// Valid case
227+
let issuance = TokenIssuanceV1 {
228+
token_ticker: random_string(&mut rng, 1..5).as_bytes().to_vec(),
229+
number_of_decimals: rng.gen_range(1..18),
230+
metadata_uri: random_string(&mut rng, 1..1024).as_bytes().to_vec(),
231+
supply_limit: TokenTotalSupply::Unlimited,
232+
reissuance_controller: Destination::AnyoneCanSpend,
233+
};
234+
let block_index = tf
235+
.make_block_builder()
236+
.add_transaction(
237+
TransactionBuilder::new()
238+
.add_input(
239+
TxInput::from_utxo(outpoint_source_id, 0),
240+
InputWitness::NoSignature(None),
241+
)
242+
.add_output(TxOutput::Transfer(
243+
issuance.clone().into(),
244+
Destination::AnyoneCanSpend,
245+
))
246+
.add_output(TxOutput::Burn(OutputValue::Coin(token_min_issuance_fee)))
247+
.build(),
248+
)
249+
.build_and_process()
250+
.unwrap()
251+
.unwrap();
252+
253+
let block = tf.block(*block_index.block_id());
254+
assert_eq!(
255+
get_output_value(&block.transactions()[0].transaction().outputs()[0]).unwrap(),
256+
issuance.into()
257+
);
258+
});
259+
}

chainstate/test-suite/src/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod delegation_tests;
3636
mod double_spend_tests;
3737
mod events_tests;
3838
mod fungible_tokens;
39+
mod fungible_tokens_v1;
3940
mod history_iteration;
4041
mod homomorphism;
4142
mod initialization;

chainstate/tx-verifier/src/transaction_verifier/input_output_policy/constraints_accumulator.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,12 @@ impl ConstrainedValueAccumulator {
163163
}
164164
OutputValue::Token(token_data) => match token_data.as_ref() {
165165
TokenData::TokenTransfer(data) => {
166-
let constrained_amount = self
167-
.burn_constrained
168-
.get_mut(&data.token_id)
169-
.ok_or(IOPolicyError::AmountOverflow)?;
170-
*constrained_amount = (*constrained_amount - data.amount)
171-
.ok_or(IOPolicyError::AmountOverflow)?;
166+
if let Some(constrained_amount) =
167+
self.burn_constrained.get_mut(&data.token_id)
168+
{
169+
*constrained_amount = (*constrained_amount - data.amount)
170+
.ok_or(IOPolicyError::AmountOverflow)?;
171+
}
172172
}
173173
TokenData::TokenIssuance(_)
174174
| TokenData::NftIssuance(_)

0 commit comments

Comments
 (0)