Skip to content

Commit

Permalink
move: upgrade command in transactional tests (MystenLabs#10128)
Browse files Browse the repository at this point in the history
## Description 

Enables `#upgrade` in transactional tests.

## Test Plan 

It is tests.
rvantonder authored Apr 13, 2023
1 parent 3cbf585 commit b60adff
Showing 7 changed files with 296 additions and 7 deletions.
13 changes: 13 additions & 0 deletions crates/sui-adapter-transactional-tests/tests/upgrade/basic.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
processed 3 tasks

init:
A: object(0,0)

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

task 2 'upgrade'. lines 13-17:
Error: Transaction Effects Status: Invalid package upgrade. New package is incompatible with previous version
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: PackageUpgradeError { upgrade_error: IncompatibleUpgrade }, source: Some(PartialVMError { major_status: BACKWARD_INCOMPATIBLE_MODULE_UPDATE, sub_status: None, message: None, exec_state: None, indices: [], offsets: [] }), command: Some(1) } }
17 changes: 17 additions & 0 deletions crates/sui-adapter-transactional-tests/tests/upgrade/basic.move
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 --addresses Test=0x0 --accounts A

//# publish --upgradeable --sender A
module Test::M1 {
use sui::tx_context::TxContext;
fun init(_ctx: &mut TxContext) { }
public fun f1() { }
}

//# upgrade --package Test --upgrade-capability 1,1 --sender A
module Test::M1 {
use sui::tx_context::TxContext;
fun init(_ctx: &mut TxContext) { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
processed 4 tasks

init:
A: object(0,0)

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

task 2 'upgrade'. lines 12-17:
created: object(2,0)
mutated: object(0,0), object(1,1)
gas summary: computation_cost: 1000000, storage_cost: 6194000, storage_rebate: 2595780, non_refundable_storage_fee: 26220

task 3 'upgrade'. lines 19-23:
Error: Transaction Effects Status: Invalid package upgrade. New package is incompatible with previous version
Execution Error: ExecutionError: ExecutionError { inner: ExecutionErrorInner { kind: PackageUpgradeError { upgrade_error: IncompatibleUpgrade }, source: Some(PartialVMError { major_status: BACKWARD_INCOMPATIBLE_MODULE_UPDATE, sub_status: None, message: None, exec_state: None, indices: [], offsets: [] }), command: Some(1) } }
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//# init --addresses Test_V1=0x0 Test_V2=0x0 Test_V3=0x0 --accounts A

//# publish --upgradeable --sender A
module Test_V1::M1 {
use sui::tx_context::TxContext;
fun init(_ctx: &mut TxContext) { }
}

//# upgrade --package Test_V1 --upgrade-capability 1,1 --sender A
module Test_V2::M1 {
use sui::tx_context::TxContext;
fun init(_ctx: &mut TxContext) { }
public fun f1() { }
}

//# upgrade --package Test_V2 --upgrade-capability 1,1 --sender A
module Test_V3::M1 {
use sui::tx_context::TxContext;
fun init(_ctx: &mut TxContext) { }
}

23 changes: 23 additions & 0 deletions crates/sui-transactional-test-runner/src/args.rs
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ use move_command_line_common::{parser::Parser as MoveCLParser, values::ValueToke
use move_core_types::identifier::Identifier;
use move_core_types::u256::U256;
use move_core_types::value::{MoveStruct, MoveValue};
use move_transactional_test_runner::tasks::SyntaxChoice;
use sui_types::base_types::SuiAddress;
use sui_types::messages::{Argument, CallArg, ObjectArg};
use sui_types::object::Owner;
@@ -95,6 +96,26 @@ pub struct ProgrammableTransactionCommand {
pub inputs: Vec<ParsedValue<SuiExtraValueArgs>>,
}

#[derive(Debug, clap::Parser)]
pub struct UpgradePackageCommand {
#[clap(long = "package")]
pub package: String,
#[clap(long = "upgrade-capability", parse(try_from_str = parse_fake_id))]
pub upgrade_capability: FakeID,
#[clap(
long = "dependencies",
multiple_values(true),
multiple_occurrences(false)
)]
pub dependencies: Vec<String>,
#[clap(long = "sender")]
pub sender: String,
#[clap(long = "gas-budget")]
pub gas_budget: Option<u64>,
#[clap(long = "syntax")]
pub syntax: Option<SyntaxChoice>,
}

#[derive(Debug, clap::Parser)]
pub enum SuiSubcommand {
#[clap(name = "view-object")]
@@ -105,6 +126,8 @@ pub enum SuiSubcommand {
ConsensusCommitPrologue(ConsensusCommitPrologueCommand),
#[clap(name = "programmable")]
ProgrammableTransaction(ProgrammableTransactionCommand),
#[clap(name = "upgrade")]
UpgradePackage(UpgradePackageCommand),
}

#[derive(Debug)]
188 changes: 185 additions & 3 deletions crates/sui-transactional-test-runner/src/test_adapter.rs
Original file line number Diff line number Diff line change
@@ -12,9 +12,11 @@ use move_command_line_common::{
address::ParsedAddress, files::verify_and_create_named_address_mapping,
};
use move_compiler::{
compiled_unit::AnnotatedCompiledUnit,
shared::{NumberFormat, NumericalAddress, PackagePaths},
Flags, FullyCompiledProgram,
};
use move_core_types::ident_str;
use move_core_types::{
account_address::AccountAddress,
identifier::IdentStr,
@@ -23,7 +25,7 @@ use move_core_types::{
};
use move_symbol_pool::Symbol;
use move_transactional_test_runner::{
framework::{CompiledState, MoveTestAdapter},
framework::{compile_ir_module, compile_source_units, CompiledState, MoveTestAdapter},
tasks::{InitCommand, SyntaxChoice, TaskInput},
};
use move_vm_runtime::{move_vm::MoveVM, session::SerializedReturnValues};
@@ -38,9 +40,12 @@ use std::{
use sui_adapter::execution_engine;
use sui_adapter::{adapter::new_move_vm, execution_mode};
use sui_core::transaction_input_checker::check_objects;
use sui_framework::{BuiltInFramework, DEFAULT_FRAMEWORK_PATH};
use sui_framework::BuiltInFramework;
use sui_framework::DEFAULT_FRAMEWORK_PATH;
use sui_protocol_config::ProtocolConfig;
use sui_types::id::UID;
use sui_types::move_package::UpgradePolicy;
use sui_types::MOVE_STDLIB_OBJECT_ID;
use sui_types::{
base_types::{ObjectID, ObjectRef, SuiAddress, TransactionDigest, SUI_ADDRESS_LENGTH},
crypto::{get_key_pair_from_rng, AccountKeyPair},
@@ -62,7 +67,10 @@ use sui_types::{
object::GAS_VALUE_FOR_TESTING,
};
use sui_types::{in_memory_storage::InMemoryStorage, messages::ProgrammableTransaction};
use sui_types::{messages::CallArg, MOVE_STDLIB_OBJECT_ID};
use sui_types::{
messages::{Argument, CallArg},
move_package::MovePackage,
};
use sui_types::{
programmable_transaction_builder::ProgrammableTransactionBuilder, SUI_FRAMEWORK_OBJECT_ID,
};
@@ -654,11 +662,185 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> {
let output = self.object_summary_output(&summary);
Ok(output)
}
SuiSubcommand::UpgradePackage(UpgradePackageCommand {
package,
upgrade_capability,
dependencies,
sender,
gas_budget,
syntax,
}) => {
let syntax = syntax.unwrap_or_else(|| self.default_syntax());
let data = data.ok_or_else(|| {
anyhow::anyhow!(
"Expected a module text block following 'upgrade' starting on lines {}-{}",
start_line,
command_lines_stop
)
})?;

let state = self.compiled_state();
let (mut modules, warnings_opt) = match syntax {
SyntaxChoice::Source => {
let (units, warnings_opt) =
compile_source_units(state, data.path(), Some(package.clone()))?;
let modules = units
.into_iter()
.map(|unit| match unit {
AnnotatedCompiledUnit::Module(annot_module) => {
let (named_addr_opt, _id) = annot_module.module_id();
let named_addr_opt = named_addr_opt.map(|n| n.value);
let module = annot_module.named_module.module;
(named_addr_opt, module)
}
AnnotatedCompiledUnit::Script(_) => panic!(
"Expected a module text block, not a script, \
following 'upgrade' starting on lines {}-{}",
start_line, command_lines_stop
),
})
.collect();
(modules, warnings_opt)
}
SyntaxChoice::IR => {
let module = compile_ir_module(state, data.path())?;
(vec![(None, module)], None)
}
};
let output = self.upgrade_package(
package,
&modules,
upgrade_capability,
dependencies,
sender,
gas_budget,
)?;
match syntax {
SyntaxChoice::Source => {
let path = data.path().to_str().unwrap().to_owned();
self.compiled_state()
.add_with_source_file(modules, (path, data))
}
SyntaxChoice::IR => {
let module = modules.pop().unwrap().1;
self.compiled_state()
.add_and_generate_interface_file(module);
}
};
Ok(merge_output(warnings_opt, output))
}
}
}
}

fn merge_output(left: Option<String>, right: Option<String>) -> Option<String> {
match (left, right) {
(None, right) => right,
(left, None) => left,
(Some(mut left), Some(right)) => {
left.push_str(&right);
Some(left)
}
}
}

impl<'a> SuiTestAdapter<'a> {
fn upgrade_package(
&mut self,
package: String,
modules: &[(Option<Symbol>, CompiledModule)],
upgrade_capability: FakeID,
dependencies: Vec<String>,
sender: String,
gas_budget: Option<u64>,
) -> anyhow::Result<Option<String>> {
let modules_bytes = modules
.iter()
.map(|(_, module)| {
let mut module_bytes = vec![];
module.serialize(&mut module_bytes)?;
Ok(module_bytes)
})
.collect::<anyhow::Result<Vec<Vec<u8>>>>()?;
let gas_budget = gas_budget.unwrap_or(DEFAULT_GAS_BUDGET);

let mut dependencies: Vec<_> = dependencies
.into_iter()
.map(|d| {
let Some(addr) = self.compiled_state.named_address_mapping.get(&d) else {
bail!("There is no published module address corresponding to name address {d}");
};
let id: ObjectID = addr.into_inner().into();
Ok(id)
})
.collect::<Result<_, _>>()?;
dependencies.extend(BuiltInFramework::all_package_ids());

let mut builder = ProgrammableTransactionBuilder::new();

SuiValue::Object(upgrade_capability).into_argument(&mut builder, self)?; // Argument::Input(0)
let upgrade_arg = builder.pure(UpgradePolicy::COMPATIBLE).unwrap();
let digest: Vec<u8> =
MovePackage::compute_digest_for_modules_and_deps(&modules_bytes, &dependencies).into();
let digest_arg = builder.pure(digest).unwrap();

let upgrade_ticket = builder.programmable_move_call(
SUI_FRAMEWORK_OBJECT_ID,
ident_str!("package").to_owned(),
ident_str!("authorize_upgrade").to_owned(),
vec![],
vec![Argument::Input(0), upgrade_arg, digest_arg],
);

let package_id = self
.compiled_state
.resolve_named_address(package.as_str())
.into();
let upgrade_receipt =
builder.upgrade(package_id, upgrade_ticket, dependencies, modules_bytes);

builder.programmable_move_call(
SUI_FRAMEWORK_OBJECT_ID,
ident_str!("package").to_owned(),
ident_str!("commit_upgrade").to_owned(),
vec![],
vec![Argument::Input(0), upgrade_receipt],
);

let pt = builder.finish();

let data =
|sender, gas| TransactionData::new_programmable(sender, vec![gas], pt, gas_budget, 1);

let transaction = self.sign_txn(Some(sender), data);
let summary = self.execute_txn(transaction, gas_budget)?;
let created_package = summary
.created
.iter()
.find_map(|id| {
let package = self.storage.get_object(id).unwrap().data.try_as_package()?;
Some(package.id())
})
.unwrap();
let package_addr = NumericalAddress::new(created_package.into_bytes(), NumberFormat::Hex);
if let Some(new_package_name) = modules[0].0 {
let prev_package = self
.compiled_state
.named_address_mapping
.insert(new_package_name.to_string(), package_addr);
match prev_package.map(|a| a.into_inner()) {
Some(addr) if addr != AccountAddress::ZERO => panic!(
"Cannot reuse named address '{}' for multiple packages. \
It should be set to 0 initially",
new_package_name
),
_ => (),
}
}
let output = self.object_summary_output(&summary);
Ok(output)
}

fn sign_txn(
&mut self,
sender: Option<String>,
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ use move_command_line_common::{
use move_compiler::{
compiled_unit::AnnotatedCompiledUnit,
diagnostics::{Diagnostics, FilesSourceText},
shared::NumericalAddress,
shared::{NumberFormat, NumericalAddress},
FullyCompiledProgram,
};
use move_core_types::{
@@ -221,7 +221,7 @@ pub trait MoveTestAdapter<'a>: Sized {
let state = self.compiled_state();
let (modules, warnings_opt) = match syntax {
SyntaxChoice::Source => {
let (units, warnings_opt) = compile_source_units(state, data.path())?;
let (units, warnings_opt) = compile_source_units(state, data.path(), None)?;
let modules = units
.into_iter()
.map(|unit| match unit {
@@ -283,7 +283,8 @@ pub trait MoveTestAdapter<'a>: Sized {
let state = self.compiled_state();
let (script, warning_opt) = match syntax {
SyntaxChoice::Source => {
let (mut units, warning_opt) = compile_source_units(state, data.path())?;
let (mut units, warning_opt) =
compile_source_units(state, data.path(), None)?;
let len = units.len();
if len != 1 {
panic!("Invalid input. Expected 1 compiled unit but got {}", len)
@@ -540,6 +541,7 @@ impl<'a> CompiledState<'a> {
pub fn compile_source_units(
state: &CompiledState,
file_name: impl AsRef<Path>,
package_name: Option<String>,
) -> Result<(Vec<AnnotatedCompiledUnit>, Option<String>)> {
fn rendered_diags(files: &FilesSourceText, diags: Diagnostics) -> Option<String> {
if diags.is_empty() {
@@ -555,10 +557,20 @@ pub fn compile_source_units(
}

use move_compiler::PASS_COMPILATION;
let mut named_address_mapping = state.named_address_mapping.clone();
if let Some(package_name) = package_name {
// When a package_name is specified, create a fresh mapping for it by
// zero-ing the address for an existing mapping. Required for upgrading
// (i.e, re-publishing) an existing named package.
named_address_mapping.insert(
package_name,
NumericalAddress::new(AccountAddress::ZERO.into_bytes(), NumberFormat::Hex),
);
};
let (mut files, comments_and_compiler_res) = move_compiler::Compiler::from_files(
vec![file_name.as_ref().to_str().unwrap().to_owned()],
state.source_files().cloned().collect::<Vec<_>>(),
state.named_address_mapping.clone(),
named_address_mapping,
)
.set_pre_compiled_lib_opt(state.pre_compiled_deps)
.set_flags(move_compiler::Flags::empty().set_sources_shadow_deps(true))

0 comments on commit b60adff

Please sign in to comment.