Skip to content

Commit

Permalink
Restrict PTBs that use Random (#16077)
Browse files Browse the repository at this point in the history
## Description 

PTBs can only have `TransferObjects` or `MergeCoins` following a command
that uses `Random`, enforced by validators when receiving a signed transaction.

## Test Plan 

New transactional tests

---
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)

- [x] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [x] 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
PTBs can only have `TransferObjects` or `MergeCoins` following a command
that uses `Random`, enforced by validators when receiving a signed transaction.
  • Loading branch information
benr-ml authored Mar 14, 2024
1 parent ce29499 commit 6d7dba0
Show file tree
Hide file tree
Showing 18 changed files with 282 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
processed 8 tasks

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

task 1 'publish'. lines 6-28:
created: object(1,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 6209200, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'programmable'. lines 29-32:
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 3 'programmable'. lines 33-37:
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 4 'programmable'. lines 38-42:
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 5 'programmable'. lines 43-49:
created: object(5,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 1976000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 6 'programmable'. lines 50-56:
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880

task 7 'programmable'. lines 57-58:
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 988000, storage_rebate: 978120, non_refundable_storage_fee: 9880
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

//# publish --sender A
module test::random {
use sui::clock::Clock;
use sui::random::Random;
use sui::transfer;
use sui::object;
use sui::tx_context:: TxContext;

public struct Obj has key, store {
id: object::UID,
}

public entry fun create(ctx: &mut TxContext) {
transfer::public_share_object(Obj { id: object::new(ctx) })
}

public fun use_clock(_clock: &Clock) {}
public fun use_random(_random: &Random) {}
public fun use_random_twice(_random1: &Random, _random2: &Random) {}
public fun use_value(_value: u64) {}
}

// Good tx - use Random
//# programmable --sender A --inputs immshared(8)
//> test::random::use_random(Input(0));

// Good tx - use Clock and then Random
//# programmable --sender A --inputs immshared(6) immshared(8) @A
//> test::random::use_clock(Input(0));
//> test::random::use_random(Input(1));

// Good tx - use value and then Random
//# programmable --sender A --inputs 10 immshared(8) @A
//> test::random::use_value(Input(0));
//> test::random::use_random(Input(1));

// Good tx - use Clock, then Random, then transfer
//# programmable --sender A --inputs 10 immshared(6) immshared(8) @B
//> SplitCoins(Gas, [Input(0)]);
//> test::random::use_clock(Input(1));
//> test::random::use_random(Input(2));
//> TransferObjects([Result(0)], Input(3));

// Good tx - use Clock, then Random, then merge
//# programmable --sender A --inputs 10 immshared(6) immshared(8) @B
//> SplitCoins(Gas, [Input(0)]);
//> test::random::use_clock(Input(1));
//> test::random::use_random(Input(2));
//> MergeCoins(Gas, [Result(0)]);

// Good tx - use Random twice in the same call
//# programmable --sender A --inputs immshared(8)
//> test::random::use_random_twice(Input(0), Input(0));
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
processed 3 tasks

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

task 1 'publish'. lines 6-15:
created: object(1,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 4240800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'programmable'. lines 16-18:
Error: Error checking transaction input objects: PostRandomCommandRestrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

//# publish --sender A
module test::random {
use sui::clock::Clock;
use sui::random::Random;

public fun use_clock(_clock: &Clock) {}
public fun use_random(_random: &Random) {}
}

// bad tx - use_random, use_clock
//# programmable --sender A --inputs immshared(8) immshared(6) @A
//> test::random::use_random(Input(0));
//> test::random::use_clock(Input(1))
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
processed 3 tasks

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

task 1 'publish'. lines 6-15:
created: object(1,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 4240800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'programmable'. lines 16-20:
Error: Error checking transaction input objects: PostRandomCommandRestrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

//# publish --sender A
module test::random {
use sui::clock::Clock;
use sui::random::Random;

public fun use_clock(_clock: &Clock) {}
public fun use_random(_random: &Random) {}
}

// bad tx - use_random, transfer, use_clock
//# programmable --sender A --inputs 10 immshared(8) immshared(6) @B
//> SplitCoins(Gas, [Input(0)]);
//> test::random::use_random(Input(1));
//> TransferObjects([Result(0)], Input(3));
//> test::random::use_clock(Input(0))
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
processed 3 tasks

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

task 1 'publish'. lines 6-14:
created: object(1,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 4081200, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'programmable'. lines 15-17:
Error: Error checking transaction input objects: PostRandomCommandRestrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

//# publish --sender A
module test::random {
use sui::random::Random;

public fun use_random(_random: &Random) {}
public fun use_value(_value: u64) {}
}

// bad tx - use_random, use_value,
//# programmable --sender A --inputs 16 immshared(8)
//> test::random::use_random(Input(1));
//> test::random::use_value(Input(0))
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
processed 3 tasks

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

task 1 'publish'. lines 6-13:
created: object(1,0)
mutated: object(0,0)
gas summary: computation_cost: 1000000, storage_cost: 3898800, storage_rebate: 0, non_refundable_storage_fee: 0

task 2 'programmable'. lines 14-16:
Error: Error checking transaction input objects: PostRandomCommandRestrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

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

//# publish --sender A
module test::random {
use sui::random::Random;

public fun use_random(_random: &Random) {}
}

// bad tx - use_random twice
//# programmable --sender A --inputs immshared(8) @A
//> test::random::use_random(Input(0));
//> test::random::use_random(Input(0));
1 change: 1 addition & 0 deletions crates/sui-open-rpc/spec/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,7 @@
"enable_group_ops_native_functions": false,
"enable_jwk_consensus_updates": false,
"enable_poseidon": false,
"enable_randomness_ptb_restrictions": false,
"end_of_epoch_transaction_supported": false,
"hardened_otw_check": false,
"include_consensus_digest_in_prologue": false,
Expand Down
13 changes: 12 additions & 1 deletion crates/sui-protocol-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const MAX_PROTOCOL_VERSION: u64 = 39;
// Version 37: Reject entry functions with mutable Random.
// Version 38: Allow skipped epochs for randomness updates.
// Version 39: Extra version to fix `test_upgrade_compatibility` simtest.
// Reject PTBs that contain invalid commands after one that uses Random.
#[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct ProtocolVersion(u64);

Expand Down Expand Up @@ -389,6 +390,10 @@ struct FeatureFlags {
// Reject functions with mutable Random.
#[serde(skip_serializing_if = "is_false")]
reject_mutable_random_on_entry_functions: bool,

// Limit PTBs that contain invalid commands after one that uses Random.
#[serde(skip_serializing_if = "is_false")]
enable_randomness_ptb_restrictions: bool,
}

fn is_false(b: &bool) -> bool {
Expand Down Expand Up @@ -1165,6 +1170,10 @@ impl ProtocolConfig {
pub fn reject_mutable_random_on_entry_functions(&self) -> bool {
self.feature_flags.reject_mutable_random_on_entry_functions
}

pub fn enable_randomness_ptb_restrictions(&self) -> bool {
self.feature_flags.enable_randomness_ptb_restrictions
}
}

#[cfg(not(msim))]
Expand Down Expand Up @@ -1913,7 +1922,9 @@ impl ProtocolConfig {
}
}
38 => {}
39 => {}
39 => {
cfg.feature_flags.enable_randomness_ptb_restrictions = true;
}
// Use this template when making changes:
//
// // modify an existing constant.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ feature_flags:
allow_receiving_object_id: true
enable_coin_deny_list: true
reject_mutable_random_on_entry_functions: true
enable_randomness_ptb_restrictions: true
max_tx_size_bytes: 131072
max_input_objects: 2048
max_size_written_objects: 5000000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ feature_flags:
allow_receiving_object_id: true
enable_coin_deny_list: true
reject_mutable_random_on_entry_functions: true
enable_randomness_ptb_restrictions: true
max_tx_size_bytes: 131072
max_input_objects: 2048
max_size_written_objects: 5000000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ feature_flags:
enable_coin_deny_list: true
enable_group_ops_native_functions: true
reject_mutable_random_on_entry_functions: true
enable_randomness_ptb_restrictions: true
max_tx_size_bytes: 131072
max_input_objects: 2048
max_size_written_objects: 5000000
Expand Down
3 changes: 2 additions & 1 deletion crates/sui-transactional-test-runner/src/test_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ use sui_types::storage::ObjectStore;
use sui_types::storage::ReadStore;
use sui_types::transaction::Command;
use sui_types::transaction::ProgrammableTransaction;
use sui_types::DEEPBOOK_PACKAGE_ID;
use sui_types::MOVE_STDLIB_PACKAGE_ID;
use sui_types::SUI_SYSTEM_ADDRESS;
use sui_types::{
Expand All @@ -95,6 +94,7 @@ use sui_types::{
};
use sui_types::{utils::to_sender_signed_transaction, SUI_SYSTEM_PACKAGE_ID};
use sui_types::{DEEPBOOK_ADDRESS, SUI_DENY_LIST_OBJECT_ID};
use sui_types::{DEEPBOOK_PACKAGE_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
use tempfile::NamedTempFile;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
Expand All @@ -113,6 +113,7 @@ const WELL_KNOWN_OBJECTS: &[ObjectID] = &[
SUI_SYSTEM_STATE_OBJECT_ID,
SUI_CLOCK_OBJECT_ID,
SUI_DENY_LIST_OBJECT_ID,
SUI_RANDOMNESS_STATE_OBJECT_ID,
];
// TODO use the file name as a seed
const RNG_SEED: [u8; 32] = [
Expand Down
3 changes: 3 additions & 0 deletions crates/sui-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,9 @@ pub enum UserInputError {
address: SuiAddress,
coin_type: String,
},

#[error("Commands following a command with Random can only be TransferObjects or MergeCoins")]
PostRandomCommandRestrictions,
}

#[derive(
Expand Down
Loading

0 comments on commit 6d7dba0

Please sign in to comment.