Skip to content

Commit 55cfdb1

Browse files
Merge pull request cardano-foundation#240 from cardano-foundation/fix/ID-501-token-dust
chore: implemented Dust protection for all minting scripts except min…
2 parents 82a2306 + a28b24c commit 55cfdb1

17 files changed

+336
-201
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ cd /root && ./bin/rly start demo --processor legacy
284284

285285
After seeing something like `/ibc.core.channel.v1.MsgTimeout`, recheck you current balance, notice that your token will be return back.
286286

287+
## Additional Resources
288+
289+
- [ELI5: What is IBC?](https://medium.com/the-interchain-foundation/eli5-what-is-ibc-def44d7b5b4c)
290+
- [IBC-Go Documentation](https://ibc.cosmos.network/v8/)
291+
- [ICS 20: The Transfer Module](https://ibc.cosmos.network/v8/apps/transfer/overview/)
292+
287293
## 🫂 Kudos to the Developers in the Cardano Ecosystem
288294

289295
This project stands on the shoulders of some incredible frameworks and tools developed by the Cardano community. Huge thanks to the developers behind these services—projects like this wouldn’t be possible without their hard work and innovation:

cardano/gateway/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ OGMIOS_ENDPOINT=http://127.0.0.1:1337
1212
CARDANO_CHAIN_HOST=127.0.0.1
1313
CARDANO_CHAIN_PORT=3001
1414
CARDANO_NETWORK_MAGIC=42
15+
1516
CARDANO_EPOCH_NONCE_GENESIS="3c1195a3fb825b2d2a6ba4e67008288ab7933a6db263a28eb9fd19b3ca05212f"
1617

1718
HANDLER_JSON_PATH=/usr/src/app/cardano/offchain/deployments/handler.json

cardano/onchain/lib/ibc/auth.ak

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use aiken/collection/dict
2+
use aiken/collection/list
23
use aiken/crypto
34
use aiken/primitive/bytearray
4-
use cardano/assets.{AssetName, PolicyId, Value, quantity_of, tokens}
5+
use cardano/assets.{AssetName, PolicyId, Value, quantity_of, tokens, zero}
56
use cardano/transaction.{Output}
67

78
pub type AuthToken {
@@ -13,6 +14,23 @@ pub fn contain_auth_token(output: Output, token: AuthToken) -> Bool {
1314
quantity_of(output.value, token.policy_id, token.name) == 1
1415
}
1516

17+
pub fn contains_only_auth_tokens(
18+
output: Output,
19+
tokens: List<AuthToken>,
20+
) -> Bool {
21+
trace @"output": output
22+
trace @"tokens": tokens
23+
let expected_value =
24+
list.foldl(
25+
tokens,
26+
zero,
27+
fn(token, acc) {
28+
assets.from_asset(token.policy_id, token.name, 1) |> assets.merge(acc)
29+
},
30+
)
31+
assets.without_lovelace(output.value) == expected_value
32+
}
33+
1634
pub fn mint_auth_token(mint: Value, token: AuthToken) -> Bool {
1735
let minted_tokens = mint |> tokens(token.policy_id)
1836

cardano/onchain/lib/ibc/auth.test.ak

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,55 @@ test mint_auth_token_fail() fail {
224224

225225
auth.mint_auth_token(mint, token)
226226
}
227+
228+
test test_contains_only_auth_tokens() {
229+
let hash_sample =
230+
#"a4a054a554354a85a54a054a554354a854a054a554a054a554a054a554a054a5"
231+
232+
let auth_token_policy_id = #"786a02f742015903c6c6fd852552d272912f4740e1584761"
233+
let auth_token_asset_name = "handler"
234+
235+
let dust_token_policy_id = #"22ebf077c0b2f5ac5a646f8ecacc66653dd9da77e75cbf67"
236+
let dust_token_asset_name = "dust"
237+
238+
let auth_token =
239+
AuthToken { policy_id: auth_token_policy_id, name: auth_token_asset_name }
240+
let valid_output =
241+
Output {
242+
address: from_script(hash_sample),
243+
value: assets.from_lovelace(3_000_000)
244+
|> add(auth_token.policy_id, auth_token.name, 1),
245+
datum: NoDatum,
246+
reference_script: None,
247+
}
248+
let invalid_output_missing =
249+
Output {
250+
address: from_script(hash_sample),
251+
value: assets.from_lovelace(3_000_000),
252+
datum: NoDatum,
253+
reference_script: None,
254+
}
255+
let invalid_output_dust =
256+
Output {
257+
address: from_script(hash_sample),
258+
value: assets.from_lovelace(3_000_000)
259+
|> add(auth_token.policy_id, auth_token.name, 1)
260+
|> add(dust_token_policy_id, dust_token_asset_name, 1),
261+
datum: NoDatum,
262+
reference_script: None,
263+
}
264+
265+
let test_cases: List<(Output, List<AuthToken>, Bool)> =
266+
[
267+
(valid_output, [auth_token], True),
268+
(invalid_output_missing, [auth_token], False),
269+
(invalid_output_dust, [auth_token], False),
270+
]
271+
272+
test_cases
273+
|> list.all(
274+
fn(case) {
275+
auth.contains_only_auth_tokens(case.1st, case.2nd) == case.3rd
276+
},
277+
)
278+
}

cardano/onchain/lib/ibc/utils/validator_utils.ak

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,15 @@ pub fn validate_mint(
217217
}
218218
}
219219

220+
pub fn find_matching_output(
221+
spent_output: Output,
222+
outputs: List<Output>,
223+
) -> Output {
224+
expect [updated_output] =
225+
list.filter(outputs, fn(output) { output.address == spent_output.address })
226+
updated_output
227+
}
228+
220229
pub fn validate_token_remain(
221230
spent_output: Output,
222231
outputs: List<Output>,

cardano/onchain/validators/minting_channel.ak

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ validator mint_channel(
152152
)
153153
expect Callback(ibc_module_callback) = ibc_module_redeemer
154154
and {
155-
valid_feature,
156-
is_status_active,
157-
is_channel_open,
158-
auth.contain_auth_token(channel_output, channel_token),
159-
ibc_module_callback == OnChanOpenInit { channel_id },
155+
valid_feature?,
156+
is_status_active?,
157+
is_channel_open?,
158+
auth.contains_only_auth_tokens(channel_output, [channel_token])?,
159+
(ibc_module_callback == OnChanOpenInit { channel_id })?,
160160
}
161161
}
162162

@@ -254,12 +254,12 @@ validator mint_channel(
254254
)
255255
expect Callback(ibc_module_callback) = ibc_module_redeemer
256256
and {
257-
connection_datum.state.state == connection_state.Open,
258-
is_channel_active,
259-
auth.contain_auth_token(channel_output, channel_token),
260-
is_channel_open,
261-
valid_channel,
262-
ibc_module_callback == OnChanOpenTry { channel_id },
257+
(connection_datum.state.state == connection_state.Open)?,
258+
is_channel_active?,
259+
auth.contains_only_auth_tokens(channel_output, [channel_token])?,
260+
is_channel_open?,
261+
valid_channel?,
262+
(ibc_module_callback == OnChanOpenTry { channel_id })?,
263263
}
264264
}
265265
}

cardano/onchain/validators/minting_client.ak

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ validator mint_client(spend_client_script_hash: Hash<Blake2b_224, Script>) {
8181
) == Active
8282

8383
and {
84-
mint |> auth.mint_auth_token(client_token),
85-
client_output |> auth.contain_auth_token(client_token),
86-
valid_output_datum,
87-
is_state_active,
84+
(mint |> auth.mint_auth_token(client_token))?,
85+
(client_output |> auth.contains_only_auth_tokens([client_token]))?,
86+
valid_output_datum?,
87+
is_state_active?,
8888
}
8989
}
9090

cardano/onchain/validators/minting_connection.ak

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ validator mint_connection(
109109
_,
110110
)
111111
and {
112-
client_state_active,
113-
auth.contain_auth_token(connection_output, connection_token),
112+
client_state_active?,
113+
auth.contains_only_auth_tokens(connection_output, [connection_token])?,
114114
connection_datum.is_conn_open_init_valid(
115115
connection_output_datum,
116116
connection_token,
117-
),
117+
)?,
118118
}
119119
}
120120
ConnOpenTry {
@@ -184,9 +184,9 @@ validator mint_connection(
184184
verify_proof_policy_id,
185185
)
186186
and {
187-
client_state_active,
188-
auth.contain_auth_token(connection_output, connection_token),
189-
is_open_connection,
187+
client_state_active?,
188+
auth.contains_only_auth_tokens(connection_output, [connection_token])?,
189+
is_open_connection?,
190190
}
191191
}
192192
}

cardano/onchain/validators/minting_handler.ak

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ validator mint_handler(
3535

3636
// handler utxo contains auth token
3737
and {
38-
inputs |> list.any(fn(input) { input.output_reference == utxo_ref }),
39-
mint |> auth.mint_auth_token(auth_token),
40-
handler_output |> auth.contain_auth_token(auth_token),
41-
valid_datum,
38+
(inputs |> list.any(fn(input) { input.output_reference == utxo_ref }))?,
39+
(mint |> auth.mint_auth_token(auth_token))?,
40+
(handler_output |> auth.contains_only_auth_tokens([auth_token]))?,
41+
valid_datum?,
4242
}
4343
}
4444

cardano/onchain/validators/minting_port.ak

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use aiken/collection/dict
12
use aiken/collection/list
23
use aiken/collection/pairs
34
use cardano/assets.{PolicyId}
4-
use cardano/transaction.{Input, NoDatum, Spend, Transaction}
5+
use cardano/transaction.{Input, NoDatum, Output, Spend, Transaction}
56
use ibc/auth.{AuthToken}
67
use ibc/core/ics_005/port_redeemer.{MintPortRedeemer}
78
use ibc/core/ics_005/types/keys as port_keys
@@ -10,6 +11,19 @@ use ibc/core/ics_025_handler_interface/handler_redeemer.{
1011
}
1112
use ibc/utils/string as string_utils
1213

14+
// Introduced to prevent high spending costs or potentially prevent tx from being executed.
15+
const max_number_of_ports = 10
16+
17+
// Checks the max number of ports is not exceeded
18+
fn valid_number_of_ports(
19+
module_output: Output,
20+
port_minting_policy_id: PolicyId,
21+
) -> Bool {
22+
let number_of_ports =
23+
module_output.value |> assets.tokens(port_minting_policy_id) |> dict.size()
24+
number_of_ports <= max_number_of_ports
25+
}
26+
1327
validator mint_port {
1428
mint(
1529
redeemer: MintPortRedeemer,
@@ -47,9 +61,10 @@ validator mint_port {
4761
trace @"mint_port: transfer module output datum": module_output.datum
4862
expect NoDatum = module_output.datum
4963
and {
50-
valid_handler_operator,
51-
auth.mint_auth_token(mint, port_token),
52-
module_output |> auth.contain_auth_token(port_token),
64+
valid_handler_operator?,
65+
auth.mint_auth_token(mint, port_token)?,
66+
(module_output |> auth.contain_auth_token(port_token))?,
67+
valid_number_of_ports(module_output, port_minting_policy_id)?,
5368
}
5469
}
5570

0 commit comments

Comments
 (0)