Skip to content

Commit

Permalink
Add deny list transactional tests (#15706)
Browse files Browse the repository at this point in the history
## Description 

This PR replaces the deny list simtest with transactional tests, which
are much easier to inspect and validate.
To make errors easier to visualize and update, this PR also introduces a
new function in the test adapter that can replace known addresses and
IDs with names/fake ids in the error string.

## Test Plan 

CI

---
If your changes are not user-facing and do not break anything, you can
skip the following section. Otherwise, please briefly describe what has
changed under the Release Notes section.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
lxfind authored Jan 18, 2024
1 parent 87b56ec commit e293763
Show file tree
Hide file tree
Showing 23 changed files with 530 additions and 430 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ Version: 3
Contents: t2::o2::Obj2 {id: sui::object::UID {id: sui::object::ID {bytes: fake(5,1)}}}

task 8 'run'. lines 93-93:
Error: Error checking transaction input objects: InvalidChildObjectArgument { child_id: 0x466fb7f9c3c2c8e5e958e3597f5b22b28656f86aa5d8092810cde4edc79d7b39, parent_id: 0x8ef41369eee2379bf085587cea4e384d7d00a6e8f975b51abd1bf014b394a87c }
Error: Error checking transaction input objects: InvalidChildObjectArgument { child_id: object(4,0), parent_id: object(5,0) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
processed 14 tasks

init:
A: object(0,0), B: object(0,1)

task 1 'publish'. lines 12-38:
created: object(1,0), object(1,1), object(1,2), object(1,3), object(1,4), object(1,5)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 18316000, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'view-object'. lines 40-40:
1,0::regulated_coin

task 3 'view-object'. lines 42-42:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::Coin<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,1)}}, balance: sui::balance::Balance<test::regulated_coin::REGULATED_COIN> {value: 10000u64}}

task 4 'view-object'. lines 44-44:
Owner: Immutable
Version: 2
Contents: sui::coin::CoinMetadata<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,2)}}, decimals: 9u8, name: std::string::String {bytes: vector[82u8, 69u8, 71u8, 85u8, 76u8, 65u8, 84u8, 69u8, 68u8, 95u8, 67u8, 79u8, 73u8, 78u8]}, symbol: std::ascii::String {bytes: vector[82u8, 67u8]}, description: std::string::String {bytes: vector[65u8, 32u8, 110u8, 101u8, 119u8, 32u8, 114u8, 101u8, 103u8, 117u8, 108u8, 97u8, 116u8, 101u8, 100u8, 32u8, 99u8, 111u8, 105u8, 110u8]}, icon_url: std::option::Option<sui::url::Url> {vec: vector[]}}

task 5 'view-object'. lines 46-46:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::DenyCap<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,3)}}}

task 6 'view-object'. lines 48-48:
Owner: Immutable
Version: 2
Contents: sui::coin::RegulatedCoinMetadata<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,4)}}, coin_metadata_object: sui::object::ID {bytes: fake(1,2)}, deny_cap_object: sui::object::ID {bytes: fake(1,3)}}

task 7 'view-object'. lines 50-52:
Owner: Immutable
Version: 2
Contents: sui::coin::TreasuryCap<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,5)}}, total_supply: sui::balance::Supply<test::regulated_coin::REGULATED_COIN> {value: 10000u64}}

task 8 'run'. lines 53-55:
created: object(8,0)
mutated: object(0,0), object(1,1)
gas summary: computation_cost: 1000000, storage_cost: 3936800, storage_rebate: 2437776, non_refundable_storage_fee: 24624

task 9 'run'. lines 56-58:
created: object(9,0), object(9,1)
mutated: object(_), 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,3)
gas summary: computation_cost: 1000000, storage_cost: 11415200, storage_rebate: 2723688, non_refundable_storage_fee: 27512

task 10 'transfer-object'. lines 59-61:
Error: Error checking transaction input objects: AddressDeniedForCoin { address: @B, coin_type: "object(1,0)::regulated_coin::REGULATED_COIN" }

task 11 'run'. lines 62-64:
Error: Error checking transaction input objects: AddressDeniedForCoin { address: @B, coin_type: "object(1,0)::regulated_coin::REGULATED_COIN" }

task 12 'run'. lines 65-67:
mutated: object(_), 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,3), object(9,1)
deleted: object(9,0)
gas summary: computation_cost: 1000000, storage_cost: 9522800, storage_rebate: 11301048, non_refundable_storage_fee: 114152

task 13 'transfer-object'. lines 68-68:
mutated: object(0,1), object(8,0)
gas summary: computation_cost: 1000000, storage_cost: 2462400, storage_rebate: 1459656, non_refundable_storage_fee: 14744
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// This test verifies the basic e2e work flow of coin deny list.
// A newly created regulated coin type should come with the deny cap object.
// Coin isser can use the deny cap to deny addresses, which will no longer be able to
// transfer the coin or use it in Move calls.
// Undeny the address will restore the original behavior.

//# init --accounts A B --addresses test=0x0

//# publish --sender A
module test::regulated_coin {
use std::option;
use sui::coin;
use sui::transfer;
use sui::tx_context;
use sui::tx_context::TxContext;

struct REGULATED_COIN has drop {}

fun init(otw: REGULATED_COIN, ctx: &mut TxContext) {
let (treasury_cap, deny_cap, metadata) = coin::create_regulated_currency(
otw,
9,
b"RC",
b"REGULATED_COIN",
b"A new regulated coin",
option::none(),
ctx
);
let coin = coin::mint(&mut treasury_cap, 10000, ctx);
transfer::public_transfer(coin, tx_context::sender(ctx));
transfer::public_transfer(deny_cap, tx_context::sender(ctx));
transfer::public_freeze_object(treasury_cap);
transfer::public_freeze_object(metadata);
}
}

//# view-object 1,0

//# view-object 1,1

//# view-object 1,2

//# view-object 1,3

//# view-object 1,4

//# view-object 1,5

// Transfer away the newly minted coin works normally.
//# run sui::pay::split_and_transfer --args object(1,1) 1 @B --type-args test::regulated_coin::REGULATED_COIN --sender A

// Deny account B.
//# run sui::coin::deny_list_add --args object(0x403) object(1,3) @B --type-args test::regulated_coin::REGULATED_COIN --sender A

// Try transfer the coin from B. This should now be denied.
//# transfer-object 8,0 --sender B --recipient A

// Try using the coin in a Move call. This should also be denied.
//# run sui::pay::split_and_transfer --args object(8,0) 1 @A --type-args test::regulated_coin::REGULATED_COIN --sender B

// Undeny account B.
//# run sui::coin::deny_list_remove --args object(0x403) object(1,3) @B --type-args test::regulated_coin::REGULATED_COIN --sender A

// This time the transfer should work.
//# transfer-object 8,0 --sender B --recipient A
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
processed 16 tasks

init:
A: object(0,0)

task 1 'publish'. lines 9-62:
created: object(1,0), object(1,1), object(1,2), object(1,3), object(1,4), object(1,5), object(1,6), object(1,7), object(1,8), object(1,9), object(1,10)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 33082800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'view-object'. lines 64-64:
1,0::{first_coin, second_coin}

task 3 'view-object'. lines 66-66:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::Coin<test::first_coin::FIRST_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,1)}}, balance: sui::balance::Balance<test::first_coin::FIRST_COIN> {value: 10000u64}}

task 4 'view-object'. lines 68-68:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::Coin<test::second_coin::SECOND_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,2)}}, balance: sui::balance::Balance<test::second_coin::SECOND_COIN> {value: 10000u64}}

task 5 'view-object'. lines 70-70:
Owner: Immutable
Version: 2
Contents: sui::coin::CoinMetadata<test::first_coin::FIRST_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,3)}}, decimals: 9u8, name: std::string::String {bytes: vector[82u8, 69u8, 71u8, 85u8, 76u8, 65u8, 84u8, 69u8, 68u8, 95u8, 67u8, 79u8, 73u8, 78u8]}, symbol: std::ascii::String {bytes: vector[82u8, 67u8]}, description: std::string::String {bytes: vector[65u8, 32u8, 110u8, 101u8, 119u8, 32u8, 114u8, 101u8, 103u8, 117u8, 108u8, 97u8, 116u8, 101u8, 100u8, 32u8, 99u8, 111u8, 105u8, 110u8]}, icon_url: std::option::Option<sui::url::Url> {vec: vector[]}}

task 6 'view-object'. lines 72-72:
Owner: Immutable
Version: 2
Contents: sui::coin::CoinMetadata<test::second_coin::SECOND_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,4)}}, decimals: 9u8, name: std::string::String {bytes: vector[82u8, 69u8, 71u8, 85u8, 76u8, 65u8, 84u8, 69u8, 68u8, 95u8, 67u8, 79u8, 73u8, 78u8]}, symbol: std::ascii::String {bytes: vector[82u8, 67u8]}, description: std::string::String {bytes: vector[65u8, 32u8, 110u8, 101u8, 119u8, 32u8, 114u8, 101u8, 103u8, 117u8, 108u8, 97u8, 116u8, 101u8, 100u8, 32u8, 99u8, 111u8, 105u8, 110u8]}, icon_url: std::option::Option<sui::url::Url> {vec: vector[]}}

task 7 'view-object'. lines 74-74:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::DenyCap<test::first_coin::FIRST_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,5)}}}

task 8 'view-object'. lines 76-76:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::DenyCap<test::second_coin::SECOND_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,6)}}}

task 9 'view-object'. lines 78-78:
Owner: Immutable
Version: 2
Contents: sui::coin::RegulatedCoinMetadata<test::first_coin::FIRST_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,7)}}, coin_metadata_object: sui::object::ID {bytes: fake(1,3)}, deny_cap_object: sui::object::ID {bytes: fake(1,5)}}

task 10 'view-object'. lines 80-80:
Owner: Immutable
Version: 2
Contents: sui::coin::RegulatedCoinMetadata<test::second_coin::SECOND_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,8)}}, coin_metadata_object: sui::object::ID {bytes: fake(1,4)}, deny_cap_object: sui::object::ID {bytes: fake(1,6)}}

task 11 'view-object'. lines 82-82:
Owner: Immutable
Version: 2
Contents: sui::coin::TreasuryCap<test::first_coin::FIRST_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,9)}}, total_supply: sui::balance::Supply<test::first_coin::FIRST_COIN> {value: 10000u64}}

task 12 'view-object'. lines 84-86:
Owner: Immutable
Version: 2
Contents: sui::coin::TreasuryCap<test::second_coin::SECOND_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,10)}}, total_supply: sui::balance::Supply<test::second_coin::SECOND_COIN> {value: 10000u64}}

task 13 'run'. lines 87-89:
created: object(13,0), object(13,1)
mutated: object(_), 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,5)
gas summary: computation_cost: 1000000, storage_cost: 11293600, storage_rebate: 2663496, non_refundable_storage_fee: 26904

task 14 'transfer-object'. lines 90-92:
Error: Error checking transaction input objects: AddressDeniedForCoin { address: @A, coin_type: "object(1,0)::first_coin::FIRST_COIN" }

task 15 'transfer-object'. lines 93-93:
mutated: object(0,0), object(1,2)
gas summary: computation_cost: 1000000, storage_cost: 2416800, storage_rebate: 2392632, non_refundable_storage_fee: 24168
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

// This test creates multiple coin types in the same module, and show that deny actions on one type does not affect
// the other type.

//# init --accounts A --addresses test=0x0

//# publish --sender A
module test::first_coin {
use std::option;
use sui::coin;
use sui::transfer;
use sui::tx_context;
use sui::tx_context::TxContext;

struct FIRST_COIN has drop {}

fun init(otw: FIRST_COIN, ctx: &mut TxContext) {
let (treasury_cap, deny_cap, metadata) = coin::create_regulated_currency(
otw,
9,
b"RC",
b"REGULATED_COIN",
b"A new regulated coin",
option::none(),
ctx
);
let coin = coin::mint(&mut treasury_cap, 10000, ctx);
transfer::public_transfer(coin, tx_context::sender(ctx));
transfer::public_transfer(deny_cap, tx_context::sender(ctx));
transfer::public_freeze_object(treasury_cap);
transfer::public_freeze_object(metadata);
}
}

module test::second_coin {
use std::option;
use sui::coin;
use sui::transfer;
use sui::tx_context;
use sui::tx_context::TxContext;

struct SECOND_COIN has drop {}

fun init(otw: SECOND_COIN, ctx: &mut TxContext) {
let (treasury_cap, deny_cap, metadata) = coin::create_regulated_currency(
otw,
9,
b"RC",
b"REGULATED_COIN",
b"A new regulated coin",
option::none(),
ctx
);
let coin = coin::mint(&mut treasury_cap, 10000, ctx);
transfer::public_transfer(coin, tx_context::sender(ctx));
transfer::public_transfer(deny_cap, tx_context::sender(ctx));
transfer::public_freeze_object(treasury_cap);
transfer::public_freeze_object(metadata);
}
}

//# view-object 1,0

//# view-object 1,1

//# view-object 1,2

//# view-object 1,3

//# view-object 1,4

//# view-object 1,5

//# view-object 1,6

//# view-object 1,7

//# view-object 1,8

//# view-object 1,9

//# view-object 1,10

// Deny account A for FIRST_COIN.
//# run sui::coin::deny_list_add --args object(0x403) object(1,5) @A --type-args test::first_coin::FIRST_COIN --sender A

// Sending away first coin from A should fail.
//# transfer-object 1,1 --sender A --recipient A

// Sending away second coin from A should not be affected, and hence will succeed.
//# transfer-object 1,2 --sender A --recipient A
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
processed 13 tasks

init:
A: object(0,0)

task 1 'publish'. lines 8-52:
created: object(1,0), object(1,1), object(1,2), object(1,3), object(1,4), object(1,5), object(1,6)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 21766400, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'view-object'. lines 54-54:
Owner: Account Address ( A )
Version: 2
Contents: test::regulated_coin::Wallet {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,0)}}}

task 3 'view-object'. lines 56-56:
1,1::regulated_coin

task 4 'view-object'. lines 58-58:
Owner: Account Address ( fake(1,0) )
Version: 2
Contents: sui::coin::Coin<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,2)}}, balance: sui::balance::Balance<test::regulated_coin::REGULATED_COIN> {value: 10000u64}}

task 5 'view-object'. lines 60-60:
Owner: Immutable
Version: 2
Contents: sui::coin::CoinMetadata<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,3)}}, decimals: 9u8, name: std::string::String {bytes: vector[82u8, 69u8, 71u8, 85u8, 76u8, 65u8, 84u8, 69u8, 68u8, 95u8, 67u8, 79u8, 73u8, 78u8]}, symbol: std::ascii::String {bytes: vector[82u8, 67u8]}, description: std::string::String {bytes: vector[65u8, 32u8, 110u8, 101u8, 119u8, 32u8, 114u8, 101u8, 103u8, 117u8, 108u8, 97u8, 116u8, 101u8, 100u8, 32u8, 99u8, 111u8, 105u8, 110u8]}, icon_url: std::option::Option<sui::url::Url> {vec: vector[]}}

task 6 'view-object'. lines 62-62:
Owner: Account Address ( A )
Version: 2
Contents: sui::coin::DenyCap<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,4)}}}

task 7 'view-object'. lines 64-64:
Owner: Immutable
Version: 2
Contents: sui::coin::RegulatedCoinMetadata<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,5)}}, coin_metadata_object: sui::object::ID {bytes: fake(1,3)}, deny_cap_object: sui::object::ID {bytes: fake(1,4)}}

task 8 'view-object'. lines 66-68:
Owner: Immutable
Version: 2
Contents: sui::coin::TreasuryCap<test::regulated_coin::REGULATED_COIN> {id: sui::object::UID {id: sui::object::ID {bytes: fake(1,6)}}, total_supply: sui::balance::Supply<test::regulated_coin::REGULATED_COIN> {value: 10000u64}}

task 9 'run'. lines 69-71:
created: object(9,0), object(9,1)
mutated: object(_), 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,4)
gas summary: computation_cost: 1000000, storage_cost: 11415200, storage_rebate: 2723688, non_refundable_storage_fee: 27512

task 10 'run'. lines 72-74:
Error: Error checking transaction input objects: AddressDeniedForCoin { address: @A, coin_type: "object(1,1)::regulated_coin::REGULATED_COIN" }

task 11 'run'. lines 75-77:
mutated: object(_), 0x0000000000000000000000000000000000000000000000000000000000000403, object(0,0), object(1,4), object(9,1)
deleted: object(9,0)
gas summary: computation_cost: 1000000, storage_cost: 9522800, storage_rebate: 11301048, non_refundable_storage_fee: 114152

task 12 'run'. lines 78-78:
mutated: object(0,0), object(1,0), object(1,2)
gas summary: computation_cost: 1000000, storage_cost: 3807600, storage_rebate: 3769524, non_refundable_storage_fee: 38076
Loading

0 comments on commit e293763

Please sign in to comment.