From 48098f065acfbb393609492c23f191ebca04bc86 Mon Sep 17 00:00:00 2001 From: Xun Li Date: Tue, 31 May 2022 18:23:46 -0700 Subject: [PATCH] Add a special system transaction that updates epoch on-chain (#2223) * Add a special system transaction type * Test that the raw tx is not accepted --- crates/sui-core/src/authority.rs | 11 +- .../sui-core/src/authority/temporary_store.rs | 25 ++- crates/sui-core/src/authority_aggregator.rs | 2 +- crates/sui-core/src/execution_engine.rs | 99 +++++---- crates/sui-core/src/gateway_types.rs | 21 ++ .../sui-core/src/transaction_input_checker.rs | 43 ++-- .../src/unit_tests/authority_tests.rs | 45 +++++ crates/sui-core/tests/staged/sui.yaml | 175 ++++++++-------- .../sources/Governance/SuiSystem.move | 4 +- crates/sui-open-rpc/samples/objects.json | 46 ++--- crates/sui-open-rpc/samples/transactions.json | 190 +++++++++--------- crates/sui-open-rpc/spec/openrpc.json | 38 ++++ crates/sui-types/src/crypto.rs | 4 + crates/sui-types/src/error.rs | 2 + crates/sui-types/src/gas.rs | 17 +- crates/sui-types/src/messages.rs | 99 +++++++-- crates/sui-types/src/sui_system_state.rs | 3 +- 17 files changed, 536 insertions(+), 288 deletions(-) diff --git a/crates/sui-core/src/authority.rs b/crates/sui-core/src/authority.rs index 9904fe3f68db8..c28fc6f81a58a 100644 --- a/crates/sui-core/src/authority.rs +++ b/crates/sui-core/src/authority.rs @@ -267,6 +267,12 @@ impl AuthorityState { &self, transaction: Transaction, ) -> Result { + // Validators should never sign an external system transaction. + fp_ensure!( + !transaction.data.kind.is_system_tx(), + SuiError::InvalidSystemTransaction + ); + let transaction_digest = *transaction.digest(); // Ensure an idempotent answer. if self.database.transaction_exists(&transaction_digest)? { @@ -433,9 +439,12 @@ impl AuthorityState { .map(|(_, obj)| obj.compute_object_reference()) .sorted() .collect(); - if !shared_object_refs.is_empty() { + if !shared_object_refs.is_empty() && !certificate.data.kind.is_system_tx() { // If the transaction contains shared objects, we need to ensure they have been scheduled // for processing by the consensus protocol. + // There is no need to go through consensus for system transactions that can + // only be executed at a time when consensus is turned off. + // TODO: Add some assert here to make sure consensus is indeed off with is_system_tx. self.check_shared_locks(&transaction_digest, &shared_object_refs) .await?; } diff --git a/crates/sui-core/src/authority/temporary_store.rs b/crates/sui-core/src/authority/temporary_store.rs index 754e872b5de02..a9222bed95ce8 100644 --- a/crates/sui-core/src/authority/temporary_store.rs +++ b/crates/sui-core/src/authority/temporary_store.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 use move_core_types::account_address::AccountAddress; -use sui_types::{event::Event, gas::SuiGasStatus}; +use sui_types::{event::Event, gas::SuiGasStatus, object::Owner}; use super::*; @@ -100,10 +100,12 @@ impl AuthorityTemporaryStore { /// For every object from active_inputs (i.e. all mutable objects), if they are not /// mutated during the transaction execution, force mutating them by incrementing the /// sequence number. This is required to achieve safety. - /// We skip the last object, which is always the gas object, because gas object will be - /// updated after this. - pub fn ensure_active_inputs_mutated(&mut self) { - for (id, _seq, _) in self.active_inputs[..self.active_inputs.len() - 1].iter() { + /// We skip the gas object, because gas object will be updated separately. + pub fn ensure_active_inputs_mutated(&mut self, gas_object_id: &ObjectID) { + for (id, _seq, _) in &self.active_inputs { + if id == gas_object_id { + continue; + } if !self.written.contains_key(id) && !self.deleted.contains_key(id) { let mut object = self.objects[id].clone(); // Active input object must be Move object. @@ -184,9 +186,16 @@ impl AuthorityTemporaryStore { transaction_digest: &TransactionDigest, transaction_dependencies: Vec, status: ExecutionStatus, - gas_object_id: &ObjectID, + gas_object_ref: ObjectRef, ) -> TransactionEffects { - let (gas_reference, gas_object) = &self.written[gas_object_id]; + // In the case of special transactions that don't require a gas object, + // we don't really care about the effects to gas, just use the input for it. + let updated_gas_object_info = if gas_object_ref.0 == ObjectID::ZERO { + (gas_object_ref, Owner::AddressOwner(SuiAddress::default())) + } else { + let (gas_reference, gas_object) = &self.written[&gas_object_ref.0]; + (*gas_reference, gas_object.owner) + }; TransactionEffects { status, shared_objects: shared_object_refs, @@ -233,7 +242,7 @@ impl AuthorityTemporaryStore { } }) .collect(), - gas_object: (*gas_reference, gas_object.owner), + gas_object: updated_gas_object_info, events: self.events.clone(), dependencies: transaction_dependencies, } diff --git a/crates/sui-core/src/authority_aggregator.rs b/crates/sui-core/src/authority_aggregator.rs index b305944be2644..c33d0b9c81a0e 100644 --- a/crates/sui-core/src/authority_aggregator.rs +++ b/crates/sui-core/src/authority_aggregator.rs @@ -1161,7 +1161,7 @@ where Err(SuiError::ErrorWhileRequestingCertificate) } - /// Find the highest sequence number that is known to a quorum of authorities. + /// Find the higgest sequence number that is known to a quorum of authorities. /// NOTE: This is only reliable in the synchronous model, with a sufficient timeout value. #[cfg(test)] async fn get_latest_sequence_number(&self, object_id: ObjectID) -> SequenceNumber { diff --git a/crates/sui-core/src/execution_engine.rs b/crates/sui-core/src/execution_engine.rs index a0f2d45c5b476..8285556e3524e 100644 --- a/crates/sui-core/src/execution_engine.rs +++ b/crates/sui-core/src/execution_engine.rs @@ -14,11 +14,13 @@ use sui_types::{ event::{Event, TransferType}, gas::{self, SuiGasStatus}, messages::{ - ExecutionStatus, MoveCall, MoveModulePublish, SingleTransactionKind, TransactionData, - TransactionEffects, TransferCoin, + CallArg, ChangeEpoch, ExecutionStatus, MoveCall, MoveModulePublish, SingleTransactionKind, + TransactionData, TransactionEffects, TransferCoin, }, object::Object, storage::{BackingPackageStore, Storage}, + sui_system_state::{ADVANCE_EPOCH_FUNCTION_NAME, SUI_SYSTEM_MODULE_NAME}, + SUI_FRAMEWORK_ADDRESS, SUI_SYSTEM_STATE_OBJECT_ID, }; use tracing::{debug, instrument, trace}; @@ -36,11 +38,11 @@ pub fn execute_transaction_to_effects( ) -> SuiResult { let mut tx_ctx = TxContext::new(&transaction_data.signer(), &transaction_digest, epoch); - let gas_object_id = transaction_data.gas_payment_object_ref().0; + let gas_object_ref = *transaction_data.gas_payment_object_ref(); let status = execute_transaction( temporary_store, transaction_data, - gas_object_id, + gas_object_ref.0, &mut tx_ctx, move_vm, native_functions, @@ -63,7 +65,7 @@ pub fn execute_transaction_to_effects( &transaction_digest, transaction_dependencies.into_iter().collect(), status, - &gas_object_id, + gas_object_ref, ); Ok(effects) } @@ -93,13 +95,6 @@ fn execute_transaction( native_functions: &NativeFunctionTable, mut gas_status: SuiGasStatus, ) -> ExecutionStatus { - let mut gas_object = temporary_store - .objects() - .get(&gas_object_id) - .expect("We constructed the object map so it should always have the gas object id") - .clone(); - trace!(?gas_object_id, "Obtained gas object"); - // We must charge object read gas inside here during transaction execution, because if this fails // we must still ensure an effect is committed and all objects versions incremented. let mut result = charge_gas_for_object_read(temporary_store, &mut gas_status); @@ -146,6 +141,30 @@ fn execute_transaction( tx_ctx, &mut gas_status, ), + SingleTransactionKind::ChangeEpoch(ChangeEpoch { + epoch, + storage_charge, + computation_charge, + }) => { + let module_id = + ModuleId::new(SUI_FRAMEWORK_ADDRESS, SUI_SYSTEM_MODULE_NAME.to_owned()); + let function = ADVANCE_EPOCH_FUNCTION_NAME.to_owned(); + adapter::execute( + move_vm, + temporary_store, + module_id, + &function, + vec![], + vec![ + CallArg::SharedObject(SUI_SYSTEM_STATE_OBJECT_ID), + CallArg::Pure(bcs::to_bytes(&epoch).unwrap()), + CallArg::Pure(bcs::to_bytes(&storage_charge).unwrap()), + CallArg::Pure(bcs::to_bytes(&computation_charge).unwrap()), + ], + &mut gas_status, + tx_ctx, + ) + } }; if result.is_err() { break; @@ -160,35 +179,41 @@ fn execute_transaction( // Make sure every mutable object's version number is incremented. // This needs to happen before `charge_gas_for_storage_changes` so that it // can charge gas for all mutated objects properly. - temporary_store.ensure_active_inputs_mutated(); - if let Err(err) = - temporary_store.charge_gas_for_storage_changes(&mut gas_status, &mut gas_object) - { - // If `result` is already `Err`, we basically have two errors at the same time. - // Users should be generally more interested in the actual execution error, so we - // let that shadow the out of gas error. Also in this case, we don't need to reset - // the `temporary_store` because `charge_gas_for_storage_changes` won't mutate - // `temporary_store` if gas charge failed. - // - // If `result` is `Ok`, now we failed when charging gas, we have to reset - // the `temporary_store` to eliminate all effects caused by the execution, - // and re-ensure all mutable objects' versions are incremented. - if result.is_ok() { - temporary_store.reset(); - temporary_store.ensure_active_inputs_mutated(); - result = Err(err); + temporary_store.ensure_active_inputs_mutated(&gas_object_id); + if !gas_status.is_unmetered() { + let mut gas_object = temporary_store + .objects() + .get(&gas_object_id) + .expect("We constructed the object map so it should always have the gas object id") + .clone(); + trace!(?gas_object_id, "Obtained gas object"); + if let Err(err) = + temporary_store.charge_gas_for_storage_changes(&mut gas_status, &mut gas_object) + { + // If `result` is already `Err`, we basically have two errors at the same time. + // Users should be generally more interested in the actual execution error, so we + // let that shadow the out of gas error. Also in this case, we don't need to reset + // the `temporary_store` because `charge_gas_for_storage_changes` won't mutate + // `temporary_store` if gas charge failed. + // + // If `result` is `Ok`, now we failed when charging gas, we have to reset + // the `temporary_store` to eliminate all effects caused by the execution, + // and re-ensure all mutable objects' versions are incremented. + if result.is_ok() { + temporary_store.reset(); + temporary_store.ensure_active_inputs_mutated(&gas_object_id); + result = Err(err); + } } + let cost_summary = gas_status.summary(result.is_ok()); + let gas_used = cost_summary.gas_used(); + let gas_rebate = cost_summary.storage_rebate; + gas::deduct_gas(&mut gas_object, gas_used, gas_rebate); + trace!(gas_used, gas_obj_id =? gas_object.id(), gas_obj_ver =? gas_object.version(), "Updated gas object"); + temporary_store.write_object(gas_object); } let cost_summary = gas_status.summary(result.is_ok()); - let gas_used = cost_summary.gas_used(); - let gas_rebate = cost_summary.storage_rebate; - gas::deduct_gas(&mut gas_object, gas_used, gas_rebate); - trace!(gas_used, gas_obj_id =? gas_object.id(), gas_obj_ver =? gas_object.version(), "Updated gas object"); - temporary_store.write_object(gas_object); - - // TODO: Return cost_summary so that the detailed summary exists in TransactionEffects for - // gas and rebate distribution. match result { Ok(()) => ExecutionStatus::Success { gas_cost: cost_summary, diff --git a/crates/sui-core/src/gateway_types.rs b/crates/sui-core/src/gateway_types.rs index d48e552f2feee..5d24bc0b7b333 100644 --- a/crates/sui-core/src/gateway_types.rs +++ b/crates/sui-core/src/gateway_types.rs @@ -23,6 +23,7 @@ use serde_json::Value; use sui_types::base_types::{ ObjectDigest, ObjectID, ObjectInfo, ObjectRef, SequenceNumber, SuiAddress, TransactionDigest, }; +use sui_types::committee::EpochId; use sui_types::crypto::{AuthorityQuorumSignInfo, Signature}; use sui_types::error::SuiError; use sui_types::event::Event; @@ -756,6 +757,8 @@ pub enum SuiTransactionKind { Publish(SuiMovePackage), /// Call a function in a published Move module Call(SuiMoveCall), + /// A system transaction that will update epoch information on-chain. + ChangeEpoch(SuiChangeEpoch), // .. more transaction types go here } @@ -789,6 +792,12 @@ impl Display for SuiTransactionKind { writeln!(writer, "Arguments : {:?}", c.arguments)?; write!(writer, "Type Arguments : {:?}", c.type_arguments)?; } + Self::ChangeEpoch(e) => { + writeln!(writer, "Transaction Kind: Epoch Change")?; + writeln!(writer, "New epoch ID: {}", e.epoch)?; + writeln!(writer, "Storage gas reward: {}", e.storage_charge)?; + writeln!(writer, "Computation gas reward: {}", e.computation_charge)?; + } } write!(f, "{}", writer) } @@ -823,6 +832,11 @@ impl TryFrom for SuiTransactionKind { }) .collect::, _>>()?, }), + SingleTransactionKind::ChangeEpoch(e) => Self::ChangeEpoch(SuiChangeEpoch { + epoch: e.epoch, + storage_charge: e.storage_charge, + computation_charge: e.computation_charge, + }), }) } } @@ -839,6 +853,13 @@ pub struct SuiMoveCall { pub arguments: Vec, } +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct SuiChangeEpoch { + pub epoch: EpochId, + pub storage_charge: u64, + pub computation_charge: u64, +} + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename = "CertifiedTransaction", rename_all = "camelCase")] pub struct SuiCertifiedTransaction { diff --git a/crates/sui-core/src/transaction_input_checker.rs b/crates/sui-core/src/transaction_input_checker.rs index be8296bf789b2..b121328a8881d 100644 --- a/crates/sui-core/src/transaction_input_checker.rs +++ b/crates/sui-core/src/transaction_input_checker.rs @@ -26,14 +26,15 @@ pub async fn check_transaction_input( where S: Eq + Serialize + for<'de> Deserialize<'de>, { - let (gas_object, mut gas_status) = check_gas( + let mut gas_status = check_gas( store, transaction.gas_payment_object_ref().0, transaction.data.gas_budget, + transaction.data.kind.is_system_tx(), ) .await?; - let objects_by_kind = check_locks(store, &transaction.data, gas_object).await?; + let objects_by_kind = check_locks(store, &transaction.data).await?; if transaction.contains_shared_object() { shared_obj_metric.inc(); @@ -55,40 +56,35 @@ async fn check_gas( store: &SuiDataStore, gas_payment_id: ObjectID, gas_budget: u64, -) -> SuiResult<(Object, SuiGasStatus<'static>)> + is_system_tx: bool, +) -> SuiResult> where S: Eq + Serialize + for<'de> Deserialize<'de>, { - let gas_object = store.get_object(&gas_payment_id)?; - let gas_object = gas_object.ok_or(SuiError::ObjectNotFound { - object_id: gas_payment_id, - })?; - gas::check_gas_balance(&gas_object, gas_budget)?; - // TODO: Pass in real computation gas unit price and storage gas unit price. - let gas_status = gas::start_gas_metering(gas_budget, 1, 1)?; - Ok((gas_object, gas_status)) + if is_system_tx { + Ok(SuiGasStatus::new_unmetered()) + } else { + let gas_object = store.get_object(&gas_payment_id)?; + let gas_object = gas_object.ok_or(SuiError::ObjectNotFound { + object_id: gas_payment_id, + })?; + gas::check_gas_balance(&gas_object, gas_budget)?; + // TODO: Pass in real computation gas unit price and storage gas unit price. + let gas_status = gas::start_gas_metering(gas_budget, 1, 1)?; + Ok(gas_status) + } } #[instrument(level = "trace", skip_all, fields(num_objects = input_objects.len()))] async fn fetch_objects( store: &SuiDataStore, input_objects: &[InputObjectKind], - gas_object_opt: Option, ) -> Result>, SuiError> where S: Eq + Serialize + for<'de> Deserialize<'de>, { let ids: Vec<_> = input_objects.iter().map(|kind| kind.object_id()).collect(); - if let Some(gas_object) = gas_object_opt { - // If we already have the gas object, avoid fetching it again. - // Skip the last one since it's the gas object. - debug_assert_eq!(gas_object.id(), ids[ids.len() - 1]); - let mut result = store.get_objects(&ids[..ids.len() - 1])?; - result.push(Some(gas_object)); - Ok(result) - } else { - store.get_objects(&ids[..]) - } + store.get_objects(&ids[..]) } /// Check all the objects used in the transaction against the database, and ensure @@ -97,14 +93,13 @@ where async fn check_locks( store: &SuiDataStore, transaction: &TransactionData, - gas_object: Object, ) -> Result, SuiError> where S: Eq + Serialize + for<'de> Deserialize<'de>, { let input_objects = transaction.input_objects()?; // These IDs act as authenticators that can own other objects. - let objects = fetch_objects(store, &input_objects, Some(gas_object)).await?; + let objects = fetch_objects(store, &input_objects).await?; // Constructing the list of objects that could be used to authenticate other // objects. Any mutable object (either shared or owned) can be used to diff --git a/crates/sui-core/src/unit_tests/authority_tests.rs b/crates/sui-core/src/unit_tests/authority_tests.rs index 61c832b82db38..4919b83a6bfff 100644 --- a/crates/sui-core/src/unit_tests/authority_tests.rs +++ b/crates/sui-core/src/unit_tests/authority_tests.rs @@ -1255,6 +1255,51 @@ async fn test_genesis_sui_sysmtem_state_object() { assert_eq!(move_object.type_, SuiSystemState::type_()); } +#[tokio::test] +async fn test_change_epoch_transaction() { + let authority_state = init_state().await; + let signed_tx = SignedTransaction::new_change_epoch( + 0, + 100, + 100, + authority_state.name, + &*authority_state.secret, + ); + // Make sure that the raw transaction will never be accepted by the validator. + assert_eq!( + authority_state + .handle_transaction(signed_tx.clone().to_transaction()) + .await + .unwrap_err(), + SuiError::InvalidSystemTransaction + ); + let mut builder = SignatureAggregator::try_new( + signed_tx.clone().to_transaction(), + &authority_state.committee, + ) + .unwrap(); + let certificate = builder + .append( + signed_tx.auth_sign_info.authority, + signed_tx.auth_sign_info.signature, + ) + .unwrap() + .unwrap(); + let result = authority_state + .handle_confirmation_transaction(ConfirmationTransaction::new(certificate)) + .await + .unwrap(); + assert!(result.signed_effects.unwrap().effects.status.is_ok()); + let sui_system_object = authority_state + .get_object(&SUI_SYSTEM_STATE_OBJECT_ID) + .await + .unwrap() + .unwrap(); + let move_object = sui_system_object.data.try_as_move().unwrap(); + let sui_system_state = bcs::from_bytes::(move_object.contents()).unwrap(); + assert_eq!(sui_system_state.epoch, 1); +} + // helpers #[cfg(test)] diff --git a/crates/sui-core/tests/staged/sui.yaml b/crates/sui-core/tests/staged/sui.yaml index ceb8fc63eaaf4..f6e2311e46dd2 100644 --- a/crates/sui-core/tests/staged/sui.yaml +++ b/crates/sui-core/tests/staged/sui.yaml @@ -40,6 +40,11 @@ CallArg: SharedObject: NEWTYPE: TYPENAME: ObjectID +ChangeEpoch: + STRUCT: + - epoch: U64 + - storage_charge: U64 + - computation_charge: U64 Data: ENUM: 0: @@ -214,6 +219,10 @@ SingleTransactionKind: Call: NEWTYPE: TYPENAME: MoveCall + 3: + ChangeEpoch: + NEWTYPE: + TYPENAME: ChangeEpoch StructTag: STRUCT: - address: @@ -339,140 +348,142 @@ SuiError: - current_sequence_number: TYPENAME: SequenceNumber 28: - UnexpectedTransactionIndex: UNIT + InvalidSystemTransaction: UNIT 29: - ConcurrentIteratorError: UNIT + UnexpectedTransactionIndex: UNIT 30: - ClosedNotifierError: UNIT + ConcurrentIteratorError: UNIT 31: + ClosedNotifierError: UNIT + 32: CertificateNotfound: STRUCT: - certificate_digest: TYPENAME: TransactionDigest - 32: + 33: ParentNotfound: STRUCT: - object_id: TYPENAME: ObjectID - sequence: TYPENAME: SequenceNumber - 33: - UnknownSenderAccount: UNIT 34: - CertificateAuthorityReuse: UNIT + UnknownSenderAccount: UNIT 35: - InvalidSequenceNumber: UNIT + CertificateAuthorityReuse: UNIT 36: - SequenceOverflow: UNIT + InvalidSequenceNumber: UNIT 37: - SequenceUnderflow: UNIT + SequenceOverflow: UNIT 38: - WrongShard: UNIT + SequenceUnderflow: UNIT 39: - InvalidCrossShardUpdate: UNIT + WrongShard: UNIT 40: - InvalidAuthenticator: UNIT + InvalidCrossShardUpdate: UNIT 41: - InvalidAddress: UNIT + InvalidAuthenticator: UNIT 42: - InvalidTransactionDigest: UNIT + InvalidAddress: UNIT 43: + InvalidTransactionDigest: UNIT + 44: InvalidObjectDigest: STRUCT: - object_id: TYPENAME: ObjectID - expected_digest: TYPENAME: ObjectDigest - 44: - InvalidDecoding: UNIT 45: - UnexpectedMessage: UNIT + InvalidDecoding: UNIT 46: - DuplicateObjectRefInput: UNIT + UnexpectedMessage: UNIT 47: + DuplicateObjectRefInput: UNIT + 48: ClientIoError: STRUCT: - error: STR - 48: - TransferImmutableError: UNIT 49: + TransferImmutableError: UNIT + 50: TooManyItemsError: NEWTYPE: U64 - 50: - InvalidSequenceRangeError: UNIT 51: - NoBatchesFoundError: UNIT + InvalidSequenceRangeError: UNIT 52: - CannotSendClientMessageError: UNIT + NoBatchesFoundError: UNIT 53: + CannotSendClientMessageError: UNIT + 54: SubscriptionItemsDroppedError: NEWTYPE: U64 - 54: - SubscriptionServiceClosed: UNIT 55: + SubscriptionServiceClosed: UNIT + 56: CheckpointingError: STRUCT: - error: STR - 56: + 57: ModuleLoadFailure: STRUCT: - error: STR - 57: + 58: ModuleVerificationFailure: STRUCT: - error: STR - 58: + 59: ModuleDeserializationFailure: STRUCT: - error: STR - 59: + 60: ModulePublishFailure: STRUCT: - error: STR - 60: + 61: ModuleBuildFailure: STRUCT: - error: STR - 61: + 62: DependentPackageNotFound: STRUCT: - package_id: TYPENAME: ObjectID - 62: + 63: MoveUnitTestFailure: STRUCT: - error: STR - 63: + 64: FunctionNotFound: STRUCT: - error: STR - 64: + 65: ModuleNotFound: STRUCT: - module_name: STR - 65: + 66: InvalidFunctionSignature: STRUCT: - error: STR - 66: + 67: InvalidFunctionVisibility: STRUCT: - error: STR - 67: + 68: TypeError: STRUCT: - error: STR - 68: + 69: AbortedExecution: STRUCT: - error: STR - 69: + 70: InvalidMoveEvent: STRUCT: - error: STR - 70: - CircularObjectOwnership: UNIT 71: + CircularObjectOwnership: UNIT + 72: InvalidSharedChildUse: STRUCT: - child: @@ -482,17 +493,17 @@ SuiError: TYPENAME: ObjectID - ancestor_module: STR - current_module: STR - 72: + 73: GasBudgetTooHigh: STRUCT: - error: STR - 73: + 74: InsufficientGas: STRUCT: - error: STR - 74: - InvalidTxUpdate: UNIT 75: + InvalidTxUpdate: UNIT + 76: TransactionLockExists: STRUCT: - refs: @@ -501,21 +512,21 @@ SuiError: - TYPENAME: ObjectID - TYPENAME: SequenceNumber - TYPENAME: ObjectDigest - 76: - TransactionLockDoesNotExist: UNIT 77: - TransactionLockReset: UNIT + TransactionLockDoesNotExist: UNIT 78: + TransactionLockReset: UNIT + 79: TransactionNotFound: STRUCT: - digest: TYPENAME: TransactionDigest - 79: + 80: ObjectNotFound: STRUCT: - object_id: TYPENAME: ObjectID - 80: + 81: ObjectDeleted: STRUCT: - object_ref: @@ -523,26 +534,26 @@ SuiError: - TYPENAME: ObjectID - TYPENAME: SequenceNumber - TYPENAME: ObjectDigest - 81: + 82: BadObjectType: STRUCT: - error: STR - 82: - MoveExecutionFailure: UNIT 83: - ObjectInputArityViolation: UNIT + MoveExecutionFailure: UNIT 84: - ExecutionInvariantViolation: UNIT + ObjectInputArityViolation: UNIT 85: - AuthorityInformationUnavailable: UNIT + ExecutionInvariantViolation: UNIT 86: - AuthorityUpdateFailure: UNIT + AuthorityInformationUnavailable: UNIT 87: + AuthorityUpdateFailure: UNIT + 88: ByzantineAuthoritySuspicion: STRUCT: - authority: TYPENAME: PublicKeyBytes - 88: + 89: PairwiseSyncFailed: STRUCT: - xsource: @@ -553,31 +564,31 @@ SuiError: TYPENAME: TransactionDigest - error: TYPENAME: SuiError - 89: + 90: StorageError: NEWTYPE: TYPENAME: TypedStoreError - 90: - BatchErrorSender: UNIT 91: + BatchErrorSender: UNIT + 92: GenericAuthorityError: STRUCT: - error: STR - 92: + 93: QuorumNotReached: STRUCT: - errors: SEQ: TYPENAME: SuiError - 93: + 94: ObjectSerializationError: STRUCT: - error: STR - 94: - ConcurrentTransactionError: UNIT 95: - IncorrectRecipientError: UNIT + ConcurrentTransactionError: UNIT 96: + IncorrectRecipientError: UNIT + 97: TooManyIncorrectAuthorities: STRUCT: - errors: @@ -585,45 +596,45 @@ SuiError: TUPLE: - TYPENAME: PublicKeyBytes - TYPENAME: SuiError - 97: + 98: InconsistentGatewayResult: STRUCT: - error: STR - 98: + 99: GatewayInvalidTxRangeQuery: STRUCT: - error: STR - 99: - OnlyOneConsensusClientPermitted: UNIT 100: + OnlyOneConsensusClientPermitted: UNIT + 101: ConsensusConnectionBroken: NEWTYPE: STR - 101: + 102: FailedToHearBackFromConsensus: NEWTYPE: STR - 102: + 103: SharedObjectLockingFailure: NEWTYPE: STR - 103: - ListenerCapacityExceeded: UNIT 104: + ListenerCapacityExceeded: UNIT + 105: ConsensusSuiSerializationError: NEWTYPE: STR - 105: - NotASharedObjectTransaction: UNIT 106: + NotASharedObjectTransaction: UNIT + 107: SignatureSeedInvalidLength: NEWTYPE: U64 - 107: + 108: HkdfError: NEWTYPE: STR - 108: + 109: SignatureKeyGenError: NEWTYPE: STR - 109: + 110: RpcError: NEWTYPE: STR - 110: + 111: UnsupportedFeatureError: STRUCT: - error: STR diff --git a/crates/sui-framework/sources/Governance/SuiSystem.move b/crates/sui-framework/sources/Governance/SuiSystem.move index 211009088e8cd..8629a2cac15ad 100644 --- a/crates/sui-framework/sources/Governance/SuiSystem.move +++ b/crates/sui-framework/sources/Governance/SuiSystem.move @@ -222,8 +222,8 @@ module Sui::SuiSystem { computation_charge: u64, ctx: &mut TxContext, ) { - // Only an active validator can make a call to this function. - assert!(ValidatorSet::is_active_validator(&self.validators, TxContext::sender(ctx)), 0); + // Validator will make a special system call with sender set as 0x0. + assert!(TxContext::sender(ctx) == @0x0, 0); let storage_reward = Balance::create_with_value(storage_charge); let computation_reward = Balance::create_with_value(computation_charge); diff --git a/crates/sui-open-rpc/samples/objects.json b/crates/sui-open-rpc/samples/objects.json index 0f04dcc5b33ce..41b257f991fdf 100644 --- a/crates/sui-open-rpc/samples/objects.json +++ b/crates/sui-open-rpc/samples/objects.json @@ -8,7 +8,7 @@ "fields": { "description": "An NFT created by the wallet Command Line Tool", "id": { - "id": "0xace69a6b04ab1901ab7629a43dd2ea8bb7cdcb28", + "id": "0xc7619788abec20bd698d301d60461001396259a5", "version": 1 }, "name": "Example NFT", @@ -16,14 +16,14 @@ } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "4RJfkN9SgLYdb0LqxBHh6lfRPicQ8FLJgzi9w2COcTo=", + "previousTransaction": "XzaoA9oTgeW7cjKHbMsma4a4Oxn6lE8qB1rHYn0Ssz8=", "storageRebate": 25, "reference": { - "objectId": "0xace69a6b04ab1901ab7629a43dd2ea8bb7cdcb28", + "objectId": "0xc7619788abec20bd698d301d60461001396259a5", "version": 1, - "digest": "UfLPgZGbHWnC2Equ1kxZT4qoUS1GBHiX0C1M2DEbTsY=" + "digest": "6VqFCPYlKh3LruU1UfD2V98AJcyMURY+4K38UCV0PoI=" } } }, @@ -36,20 +36,20 @@ "fields": { "balance": 100000, "id": { - "id": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "id": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 0 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "previousTransaction": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "storageRebate": 0, "reference": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 0, - "digest": "hP/CkKyJQiRL53Foa/ipZyyO35R923f9+5Wz2h/F7ek=" + "digest": "qPh5rxDZL0+8iAPpOYm+MKA1kb4u2Oz/ggOYzk1+ypc=" } } }, @@ -59,16 +59,16 @@ "data": { "dataType": "package", "disassembled": { - "M1": "// Move bytecode v5\nmodule 738ca084a8e9213ec3f5662cb5c6fee1ebd0799b.M1 {\nstruct Forge has store, key {\n\tid: VersionedID,\n\tswords_created: u64\n}\nstruct Sword has store, key {\n\tid: VersionedID,\n\tmagic: u64,\n\tstrength: u64\n}\n\ninit(Arg0: &mut TxContext) {\nB0:\n\t0: CopyLoc[0](Arg0: &mut TxContext)\n\t1: Call[6](new_id(&mut TxContext): VersionedID)\n\t2: LdU64(0)\n\t3: Pack[0](Forge)\n\t4: StLoc[1](loc0: Forge)\n\t5: MoveLoc[1](loc0: Forge)\n\t6: MoveLoc[0](Arg0: &mut TxContext)\n\t7: FreezeRef\n\t8: Call[7](sender(&TxContext): address)\n\t9: Call[0](transfer(Forge, address))\n\t10: Ret\n}\npublic magic(Arg0: &Sword): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Sword)\n\t1: ImmBorrowField[0](Sword.magic: u64)\n\t2: ReadRef\n\t3: Ret\n}\npublic strength(Arg0: &Sword): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Sword)\n\t1: ImmBorrowField[1](Sword.strength: u64)\n\t2: ReadRef\n\t3: Ret\n}\npublic(script) sword_create(Arg0: &mut Forge, Arg1: u64, Arg2: u64, Arg3: address, Arg4: &mut TxContext) {\nB0:\n\t0: MoveLoc[4](Arg4: &mut TxContext)\n\t1: Call[6](new_id(&mut TxContext): VersionedID)\n\t2: MoveLoc[1](Arg1: u64)\n\t3: MoveLoc[2](Arg2: u64)\n\t4: Pack[1](Sword)\n\t5: StLoc[5](loc0: Sword)\n\t6: MoveLoc[5](loc0: Sword)\n\t7: MoveLoc[3](Arg3: address)\n\t8: Call[1](transfer(Sword, address))\n\t9: CopyLoc[0](Arg0: &mut Forge)\n\t10: ImmBorrowField[2](Forge.swords_created: u64)\n\t11: ReadRef\n\t12: LdU64(1)\n\t13: Add\n\t14: MoveLoc[0](Arg0: &mut Forge)\n\t15: MutBorrowField[2](Forge.swords_created: u64)\n\t16: WriteRef\n\t17: Ret\n}\npublic(script) sword_transfer(Arg0: Sword, Arg1: address, Arg2: &mut TxContext) {\nB0:\n\t0: MoveLoc[0](Arg0: Sword)\n\t1: MoveLoc[1](Arg1: address)\n\t2: Call[1](transfer(Sword, address))\n\t3: Ret\n}\npublic swords_created(Arg0: &Forge): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Forge)\n\t1: ImmBorrowField[2](Forge.swords_created: u64)\n\t2: ReadRef\n\t3: Ret\n}\n}" + "M1": "// Move bytecode v5\nmodule 1373663d46fa4ec26a7a434825c72dbe9c1c7145.M1 {\nstruct Forge has store, key {\n\tid: VersionedID,\n\tswords_created: u64\n}\nstruct Sword has store, key {\n\tid: VersionedID,\n\tmagic: u64,\n\tstrength: u64\n}\n\ninit(Arg0: &mut TxContext) {\nB0:\n\t0: CopyLoc[0](Arg0: &mut TxContext)\n\t1: Call[6](new_id(&mut TxContext): VersionedID)\n\t2: LdU64(0)\n\t3: Pack[0](Forge)\n\t4: StLoc[1](loc0: Forge)\n\t5: MoveLoc[1](loc0: Forge)\n\t6: MoveLoc[0](Arg0: &mut TxContext)\n\t7: FreezeRef\n\t8: Call[7](sender(&TxContext): address)\n\t9: Call[0](transfer(Forge, address))\n\t10: Ret\n}\npublic magic(Arg0: &Sword): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Sword)\n\t1: ImmBorrowField[0](Sword.magic: u64)\n\t2: ReadRef\n\t3: Ret\n}\npublic strength(Arg0: &Sword): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Sword)\n\t1: ImmBorrowField[1](Sword.strength: u64)\n\t2: ReadRef\n\t3: Ret\n}\npublic(script) sword_create(Arg0: &mut Forge, Arg1: u64, Arg2: u64, Arg3: address, Arg4: &mut TxContext) {\nB0:\n\t0: MoveLoc[4](Arg4: &mut TxContext)\n\t1: Call[6](new_id(&mut TxContext): VersionedID)\n\t2: MoveLoc[1](Arg1: u64)\n\t3: MoveLoc[2](Arg2: u64)\n\t4: Pack[1](Sword)\n\t5: StLoc[5](loc0: Sword)\n\t6: MoveLoc[5](loc0: Sword)\n\t7: MoveLoc[3](Arg3: address)\n\t8: Call[1](transfer(Sword, address))\n\t9: CopyLoc[0](Arg0: &mut Forge)\n\t10: ImmBorrowField[2](Forge.swords_created: u64)\n\t11: ReadRef\n\t12: LdU64(1)\n\t13: Add\n\t14: MoveLoc[0](Arg0: &mut Forge)\n\t15: MutBorrowField[2](Forge.swords_created: u64)\n\t16: WriteRef\n\t17: Ret\n}\npublic(script) sword_transfer(Arg0: Sword, Arg1: address, Arg2: &mut TxContext) {\nB0:\n\t0: MoveLoc[0](Arg0: Sword)\n\t1: MoveLoc[1](Arg1: address)\n\t2: Call[1](transfer(Sword, address))\n\t3: Ret\n}\npublic swords_created(Arg0: &Forge): u64 {\nB0:\n\t0: MoveLoc[0](Arg0: &Forge)\n\t1: ImmBorrowField[2](Forge.swords_created: u64)\n\t2: ReadRef\n\t3: Ret\n}\n}" } }, "owner": "Immutable", - "previousTransaction": "VRU1ozQyeBnYwhYMy4mvCY4PLNjeJlh6sKb3NuLT+Es=", + "previousTransaction": "7YHe5fURQLuk5Xq11j6XMBUqOJkXn4vBE/poS/+bHNc=", "storageRebate": 0, "reference": { - "objectId": "0x738ca084a8e9213ec3f5662cb5c6fee1ebd0799b", + "objectId": "0x1373663d46fa4ec26a7a434825c72dbe9c1c7145", "version": 1, - "digest": "3mUamQPf54x0zunKsfo146+p5f+uZg/OerDuBP6Bn2o=" + "digest": "R+bc5vA6D4BBElukzvcOi0C3UYS59b3SeuyWVXlSsh4=" } } }, @@ -77,21 +77,21 @@ "details": { "data": { "dataType": "moveObject", - "type": "0x1fd54b43eeb2c949141c4243af5c99f24bb1685e::Hero::Hero", + "type": "0xe62268a2843b18a12e8e33d910772f4dfc50e7b6::Hero::Hero", "fields": { "experience": 0, - "game_id": "0xac9e2ebfa2cd8408e94db8eb09f8299fdee18e76", + "game_id": "0x75759a6308618eb807c41b1cf17dca8dbd928fe5", "hp": 100, "id": { - "id": "0x5263a3416ab02247d4de3925632b0c1595e4462f", + "id": "0x7993eb2c32a78548faed550102b0001504cda649", "version": 1 }, "sword": { - "type": "0x1fd54b43eeb2c949141c4243af5c99f24bb1685e::Hero::Sword", + "type": "0xe62268a2843b18a12e8e33d910772f4dfc50e7b6::Hero::Sword", "fields": { - "game_id": "0xac9e2ebfa2cd8408e94db8eb09f8299fdee18e76", + "game_id": "0x75759a6308618eb807c41b1cf17dca8dbd928fe5", "id": { - "id": "0x870c23203087a5d40869160e24fe343d59dd4926", + "id": "0x1f2786131fd0911fd8e95a1489088b569eb4a343", "version": 0 }, "magic": 10, @@ -101,14 +101,14 @@ } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "njO2jDkzYLEU6a+lqQjHPx3bU0dtmlKWcK1YGqJNVm4=", + "previousTransaction": "brtqfhPIP5lgMOlTKqAhsS2rNdzDQ0EwsYoEs0adKDM=", "storageRebate": 22, "reference": { - "objectId": "0x5263a3416ab02247d4de3925632b0c1595e4462f", + "objectId": "0x7993eb2c32a78548faed550102b0001504cda649", "version": 1, - "digest": "Igz5GNhawpkzK4VW+fQaUfom4/Sy7yP0hyRyE8bYUoM=" + "digest": "Z/aJJbHLcblffUjOXy+lMPoTt+p8PWXo2vZ6168jT54=" } } } diff --git a/crates/sui-open-rpc/samples/transactions.json b/crates/sui-open-rpc/samples/transactions.json index 6e3b6291267da..1026afe854e5f 100644 --- a/crates/sui-open-rpc/samples/transactions.json +++ b/crates/sui-open-rpc/samples/transactions.json @@ -2,7 +2,7 @@ "move_call": { "EffectResponse": { "certificate": { - "transactionDigest": "4RJfkN9SgLYdb0LqxBHh6lfRPicQ8FLJgzi9w2COcTo=", + "transactionDigest": "XzaoA9oTgeW7cjKHbMsma4a4Oxn6lE8qB1rHYn0Ssz8=", "data": { "transactions": [ { @@ -10,7 +10,7 @@ "package": { "objectId": "0x0000000000000000000000000000000000000002", "version": 1, - "digest": "kr2zPqqWdlrIahQ6OShj8JP6gfLFr0Jtu16UBchzHqg=" + "digest": "faGXBqbeg7QrbM02bplusUTwaATxw4gro+QZOkmehfU=" }, "module": "DevNetNFT", "function": "mint", @@ -22,29 +22,29 @@ } } ], - "sender": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0", + "sender": "0xc8507421face4ab67dac23c9295e6eaa0d44d977", "gasPayment": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 0, - "digest": "hP/CkKyJQiRL53Foa/ipZyyO35R923f9+5Wz2h/F7ek=" + "digest": "qPh5rxDZL0+8iAPpOYm+MKA1kb4u2Oz/ggOYzk1+ypc=" }, "gasBudget": 10000 }, - "txSignature": "nAg7i808BGL8En2dlmtY0eDRf9MwxH8UrITzDTz0U9uauuFELdp+d1W5bCUShu0IphUyCLue49+s102NudrpCcXLOZYaM0tWNwkBrMYMF4+SPQP5X8qr76++SGb2P/g2", + "txSignature": "ve7SSIj9cu/HoCN0C4IoJMY1qxko6XtC7J4X/s/wLyXjXjs0yvWt7YON2UOUyMjYYJrgHgxxiRMixnWvb87/BgGrrCrtyO8uCxryrgPNFiqiCFkAitbGVvGxaxXrht0Z", "authSignInfo": { "epoch": 0, "signatures": [ [ - "OsdFmw6Cyj3sZAfyRNXI3LjUdy0FuYJEt2MgPIMBZlg=", - "OlReitTe5VMvF+CHjjd3MR9Ta39NaJ9pttr5ZxnbQavJyS0edWRlsnmotN0ZgsFJZDWHgHfvAz22f/KGvbAmDw==" + "OwIHxzZW4z+pZ+8BdE3CfShhfP562+PYWKkAEOlzifQ=", + "ETBAJaVMiZs2AZE+zZ+jEmm7F+WrokTsEPOJikpDliUWhYS7us0WEaMqUCy3U0zrPbJUvYVuifZqD+H20Ev+DA==" ], [ - "X6umn7YuYGKTQ0sYXXiinvyMx9tnwIov262H58+JrkQ=", - "/a78cRINeUhO+C46eI8ukx2BR15AO4VVPMZGHywMoR24QN1yiUN01Ygz1pZRs1z9XAksBUsfaje9i0VARbOvBQ==" + "2PFDMHzs06bcc5NyvD35PNZdVUN3OrpL2HQQZdltq8A=", + "KC8mgUigWg0qdT8UGAx32FOWUzafYDtCPMKYel1jyCz3TxTNh66dFLasBWt9AnM3Z+TcGtw482y2xd8yb+g9CQ==" ], [ - "qKeDQbZ/M4mM1E+z9dCBihx+Vkl1ZPGP3MI+qg7UarA=", - "coCyELYkh+FFHALJDqiBNhJPHi7ZL05kSHqwGLnH1yaHsIiyxRbjLpgvuXyUiwSZH4QFzrrMYuhBEsa9keRvDw==" + "uq+MQgMi/MD7G/AmKDQj920/Izb/l8ZDGvFVXqw8aFY=", + "rtIt0BvJCXFOIEmnsfnGO99F8hPV9vkTjDoLpaBhyyyTp4SGGedr4FgaS2GXD+mhJzGHFYGL3t6Nfvw587HvDA==" ] ] } @@ -58,39 +58,39 @@ "storageRebate": 0 } }, - "transactionDigest": "4RJfkN9SgLYdb0LqxBHh6lfRPicQ8FLJgzi9w2COcTo=", + "transactionDigest": "XzaoA9oTgeW7cjKHbMsma4a4Oxn6lE8qB1rHYn0Ssz8=", "created": [ { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0xace69a6b04ab1901ab7629a43dd2ea8bb7cdcb28", + "objectId": "0xc7619788abec20bd698d301d60461001396259a5", "version": 1, - "digest": "UfLPgZGbHWnC2Equ1kxZT4qoUS1GBHiX0C1M2DEbTsY=" + "digest": "6VqFCPYlKh3LruU1UfD2V98AJcyMURY+4K38UCV0PoI=" } } ], "mutated": [ { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 1, - "digest": "4NkPP0IW3nuJoV5lRFX1+k6cNAl45uhNpyVxKuujK2E=" + "digest": "IX9/I/WUo5yWh9xcljLR5UXV5LOLoKdh4mUMOw9cKv0=" } } ], "gasObject": { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 1, - "digest": "4NkPP0IW3nuJoV5lRFX1+k6cNAl45uhNpyVxKuujK2E=" + "digest": "IX9/I/WUo5yWh9xcljLR5UXV5LOLoKdh4mUMOw9cKv0=" } } } @@ -99,43 +99,43 @@ "transfer": { "EffectResponse": { "certificate": { - "transactionDigest": "S6Llzlppt95P5Oix4D+DRGIlOsuWSl7/D18Zz2rHdCs=", + "transactionDigest": "AiY+cCKq5VB8P9aEcZEJBp3goZahgl9jMkpksYUyimY=", "data": { "transactions": [ { "TransferCoin": { - "recipient": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0", + "recipient": "0xc8507421face4ab67dac23c9295e6eaa0d44d977", "objectRef": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 4, - "digest": "LRkb2MU98ZoP1AsKyKkG032sA9CfYHhtAuCX++cRiR4=" + "digest": "vDR8qSCCPtbDYJ0ZKn5FBc/ZpgiuMnENr0PJT8oKqro=" } } } ], - "sender": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0", + "sender": "0xc8507421face4ab67dac23c9295e6eaa0d44d977", "gasPayment": { - "objectId": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "objectId": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 1, - "digest": "h5w9GMJaEnShGkwLnKoF+I1W8G+jZdA67UK9OVNZmFo=" + "digest": "mFrWOvLVMJ4BPTnEHNLFlHceYNQFaJ7ndkde44nJXl8=" }, "gasBudget": 1000 }, - "txSignature": "FZIqLEG3mgBRFtKisvsBeenSCbkcfGscvi9vPJZ8kdVrNn19lGeHl0mvFIpxfGk4R3qtD5ahntUkrPtJ6CC+AcXLOZYaM0tWNwkBrMYMF4+SPQP5X8qr76++SGb2P/g2", + "txSignature": "rbgmGg3Z0fEGE6JZ6FGvDWWffExw22yL0yJhaZWK1XQV7hBrgRKQN6oaW8DoXuLGuOwNGz4EvkYVZkPw0jcKAQGrrCrtyO8uCxryrgPNFiqiCFkAitbGVvGxaxXrht0Z", "authSignInfo": { "epoch": 0, "signatures": [ [ - "X6umn7YuYGKTQ0sYXXiinvyMx9tnwIov262H58+JrkQ=", - "pAWSUeT++G7SrWgrYxk/rOCYjyCr8gocws+UxyckkpaUUE8Vac04TURINMkM6WAURHLoHCiJYy4ITw/VnJNlBg==" + "2PFDMHzs06bcc5NyvD35PNZdVUN3OrpL2HQQZdltq8A=", + "DLai1/aIiTHRgSELqvQQP2//QcwcC7HICiWo9ivdq3RKWU9Ojy9mNk5RzjtYeJpDknrV2EpFBfXtcxsHPU6fAA==" ], [ - "OsdFmw6Cyj3sZAfyRNXI3LjUdy0FuYJEt2MgPIMBZlg=", - "TWa8K88nUbcUiRX0esQWmERbKGukSSlV8z97dQFThhIlQk3wFPTuGce1CIpKSeNPsMxGEpy9qT/OLfnyCOCKBw==" + "kFejcuHXsZ8hOE9Di2LWnHqsUPJswXtakWBSNXr3h1w=", + "R/jJcDM7/RrCOs5QB99ewjrp4pjL0w+BBL60ae1dfMzrC+MnX91PV7/w/eWgZzZzOTt5GZJ6D+9Xrdafa5SgBg==" ], [ - "zSPGYAEE/x8TnuKUBylckOTEI6+bZBjrGxGmGYmTJ60=", - "sfsXwVSaNkB21YCnC3NXcjsHN8QuhyI55+2WBMTLa32E6Jza5g0K30+JHEF4yxU4L4RaUeKMOWgUxlzECw3+CA==" + "OwIHxzZW4z+pZ+8BdE3CfShhfP562+PYWKkAEOlzifQ=", + "PoZAizZuUBOh3nxL7zkdoDzXUOUd2KDAMBxc2uNpThp2znb2YF5iYLa3q6R5WMUZV445EQkY+iX7BOTod67HAQ==" ] ] } @@ -149,41 +149,41 @@ "storageRebate": 30 } }, - "transactionDigest": "S6Llzlppt95P5Oix4D+DRGIlOsuWSl7/D18Zz2rHdCs=", + "transactionDigest": "AiY+cCKq5VB8P9aEcZEJBp3goZahgl9jMkpksYUyimY=", "mutated": [ { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 5, - "digest": "PKHVrOxygr6ljUsQPt0D8bhnbco/h/Gor69P148WlOw=" + "digest": "dlCKhiTYu0E+6WZtPVh/VmaiNIN3Ddm2VS13G2CKCg8=" } }, { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "objectId": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 2, - "digest": "T8bIO1L1liTj+eDunlaX+lbxSFLpvHy4XeCgV/NL0Nw=" + "digest": "aBnjtKhgx2o9VRZw4bOR6dQHLZVgEIWV/zVUdkLRJqU=" } } ], "gasObject": { "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, "reference": { - "objectId": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "objectId": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 2, - "digest": "T8bIO1L1liTj+eDunlaX+lbxSFLpvHy4XeCgV/NL0Nw=" + "digest": "aBnjtKhgx2o9VRZw4bOR6dQHLZVgEIWV/zVUdkLRJqU=" } }, "dependencies": [ - "njO2jDkzYLEU6a+lqQjHPx3bU0dtmlKWcK1YGqJNVm4=" + "brtqfhPIP5lgMOlTKqAhsS2rNdzDQ0EwsYoEs0adKDM=" ] } } @@ -191,7 +191,7 @@ "coin_split": { "SplitCoinResponse": { "certificate": { - "transactionDigest": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "transactionDigest": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "data": { "transactions": [ { @@ -199,7 +199,7 @@ "package": { "objectId": "0x0000000000000000000000000000000000000002", "version": 1, - "digest": "kr2zPqqWdlrIahQ6OShj8JP6gfLFr0Jtu16UBchzHqg=" + "digest": "faGXBqbeg7QrbM02bplusUTwaATxw4gro+QZOkmehfU=" }, "module": "Coin", "function": "split_vec", @@ -207,7 +207,7 @@ "0x2::SUI::SUI" ], "arguments": [ - "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", [ 20, 20, @@ -219,29 +219,29 @@ } } ], - "sender": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0", + "sender": "0xc8507421face4ab67dac23c9295e6eaa0d44d977", "gasPayment": { - "objectId": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "objectId": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 2, - "digest": "T8bIO1L1liTj+eDunlaX+lbxSFLpvHy4XeCgV/NL0Nw=" + "digest": "aBnjtKhgx2o9VRZw4bOR6dQHLZVgEIWV/zVUdkLRJqU=" }, "gasBudget": 1000 }, - "txSignature": "7HAzknE6lMyjfW8B+0qJzmZn25H7WHxOCm9qE/ULUIi3Tu/b+JuTW7aLoQ678uPS3Bpm6472Rxb7Tos/9syvD8XLOZYaM0tWNwkBrMYMF4+SPQP5X8qr76++SGb2P/g2", + "txSignature": "USwotv5HY28EEGXuw+M2Qcc4FyjTlo+bYzT12PBeFlcpZqrs/WCjUrY2UozXPPPN6PBW3E4UD6vPe+unlat/AAGrrCrtyO8uCxryrgPNFiqiCFkAitbGVvGxaxXrht0Z", "authSignInfo": { "epoch": 0, "signatures": [ [ - "X6umn7YuYGKTQ0sYXXiinvyMx9tnwIov262H58+JrkQ=", - "/yMFU0qBYCsqmnCU3uDoANjklbbLrZdwRe5TWv53X94HNvUkQiXdauqHewMp8p2nPRMnkaSnoHHD1QPdRRpvCA==" + "uq+MQgMi/MD7G/AmKDQj920/Izb/l8ZDGvFVXqw8aFY=", + "6cX6z3TDJpW9NHbgBo02SjdAK0cEZTgPaHH0vdK8gdacVkeqdkOCGGsmV/x5rhR8YcZyEGSJoB737PAKgEmyAA==" ], [ - "qKeDQbZ/M4mM1E+z9dCBihx+Vkl1ZPGP3MI+qg7UarA=", - "whGgWcaxC2TR+pPELkz5pwBhO6GNXXJlT0BhC3/m8c2vVr2x0c0Q2yAInQF+Yy3g6Gwzwp2FOwNyQjAISDyMDw==" + "OwIHxzZW4z+pZ+8BdE3CfShhfP562+PYWKkAEOlzifQ=", + "zwLrxSmMJcVIijM4Nz/wBWtFsMe/E6YOv8JaZnV+02P5wKu1Fvf1EMoI2xQgM9BXCIIMxyMzSbOnXY8G8c6yBg==" ], [ - "OsdFmw6Cyj3sZAfyRNXI3LjUdy0FuYJEt2MgPIMBZlg=", - "kjt5cocQqwxDwS1HNqrcBWqRBmO0buc1KiOADjuY4NP8+UI9mo8QEcqJAo1tMTUadCwtk35SHiBvfveBRciDDQ==" + "kFejcuHXsZ8hOE9Di2LWnHqsUPJswXtakWBSNXr3h1w=", + "BI9ROm+/DAmzMtMze1zCK1y+M2/dPfw6qAJkc3yAq3pgvIHwv84kUQhFWxIdrhhqs+jo9ufPB5z1sTUAM0miBA==" ] ] } @@ -253,20 +253,20 @@ "fields": { "balance": 95681, "id": { - "id": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "id": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 6 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0x5b24fc22b7ff2a14a9c8ff104ec8390371939045", + "objectId": "0x3818c35255ad136f52e0fd23667e16e3c0cc4860", "version": 6, - "digest": "I4qLwYFw64xHKEmMmueVyKOGUOLHoig4Q1Ux1gKjzWk=" + "digest": "mjjEIWYdjwEGdWvjuuofQv8h7UoyNzeIuB/jmS3b/t4=" } }, "newCoins": [ @@ -277,20 +277,20 @@ "fields": { "balance": 20, "id": { - "id": "0x1534db04d30293afb555ab7334199982f31fcb40", + "id": "0x06958d434af8a52c36b7da24414f8ec9acec40cb", "version": 1 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0x1534db04d30293afb555ab7334199982f31fcb40", + "objectId": "0x06958d434af8a52c36b7da24414f8ec9acec40cb", "version": 1, - "digest": "9Xi5IhQNpwZGduSna7GtmysMcabbVD1VpzembtM8Whc=" + "digest": "Hs5rGAG8MSEVGYoBP9Gqzs6FF/exPFBnf7fGgkjOOr4=" } }, { @@ -300,20 +300,20 @@ "fields": { "balance": 20, "id": { - "id": "0x1ab328cf4449209bc8d72644c795f73a5e6a8df7", + "id": "0x2920ae9b8cbc28f98ca5f0b65b6db88ecc176c02", "version": 1 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0x1ab328cf4449209bc8d72644c795f73a5e6a8df7", + "objectId": "0x2920ae9b8cbc28f98ca5f0b65b6db88ecc176c02", "version": 1, - "digest": "kw7Bn5rwQGcBeqkWxxQqdEsPkAZ6LkLrZ+9u2pk4A4E=" + "digest": "bRR3Vn0oEZ1mmM27TkXgAxeOIgOE25iiUxSy2FPkXUY=" } }, { @@ -323,20 +323,20 @@ "fields": { "balance": 20, "id": { - "id": "0x1e42cc4cd67149f2c284a83674771cbdc6b791a9", + "id": "0x7ea7a5f255a828458c49988f171f298051ec36ce", "version": 1 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0x1e42cc4cd67149f2c284a83674771cbdc6b791a9", + "objectId": "0x7ea7a5f255a828458c49988f171f298051ec36ce", "version": 1, - "digest": "ufRpFXLoLaZr4G/iYLogwLD5sNgSeV/CLg0wXG7erlE=" + "digest": "C2OSTZYh+wPhCScON0n2+g2fS9TQA15MsyhvslBOf+c=" } }, { @@ -346,20 +346,20 @@ "fields": { "balance": 20, "id": { - "id": "0xa2c760d074a0ba312c842961da221131df85c08e", + "id": "0x969213935861f7abc02072ba9de1bf420db3c2de", "version": 1 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0xa2c760d074a0ba312c842961da221131df85c08e", + "objectId": "0x969213935861f7abc02072ba9de1bf420db3c2de", "version": 1, - "digest": "Hb07rPSAB8TQssHTqIomYhHgZYPgm63gevg+UpttXpU=" + "digest": "O37OLUmNAd+EefJUb0eiDNbYrNKSmaCqEQkeO5gfUZw=" } }, { @@ -369,20 +369,20 @@ "fields": { "balance": 20, "id": { - "id": "0xb70283782c13fb46371882e75f72a00f336489d0", + "id": "0xb252dec7c0fff07ab8515d5b3c832636c4ddf80a", "version": 1 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0xb70283782c13fb46371882e75f72a00f336489d0", + "objectId": "0xb252dec7c0fff07ab8515d5b3c832636c4ddf80a", "version": 1, - "digest": "sx2hyK0DflvbOk3zZtBiVwrItEosNhviXM0jld1B2gE=" + "digest": "+9JPMpAZWUUvW7ZfkjQTX6SPNLCSDNsE046C97PZDME=" } } ], @@ -393,20 +393,20 @@ "fields": { "balance": 98969, "id": { - "id": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "id": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 3 } } }, "owner": { - "AddressOwner": "0x215592226abfec8d03fbbeb8b30eb0d2129c94b0" + "AddressOwner": "0xc8507421face4ab67dac23c9295e6eaa0d44d977" }, - "previousTransaction": "NNzC24j5xf+k194cp6hQFxKVbVvbtmAJVsOU/aAai7I=", + "previousTransaction": "yLPmNAteC/bDESd72yKNdRF4/xTIYn0a1qA1MqfpEDM=", "storageRebate": 15, "reference": { - "objectId": "0x6ee2fa6563d9668349a5cd30223a68fafefbd37d", + "objectId": "0x44116984c6e384738f9abe08554a2df82bf7f81a", "version": 3, - "digest": "0hb34DiRbtuv5hDm+K83DwpqNSXpWbPlOp6mRVUfgIs=" + "digest": "jpWDZHVNvcfBBEr9aSGVH2V+hAjuiP6Wtg0Yf9EmNO4=" } } } diff --git a/crates/sui-open-rpc/spec/openrpc.json b/crates/sui-open-rpc/spec/openrpc.json index dad3b42e0a130..8571385490e7b 100644 --- a/crates/sui-open-rpc/spec/openrpc.json +++ b/crates/sui-open-rpc/spec/openrpc.json @@ -1689,6 +1689,31 @@ "SuiAddress": { "$ref": "#/components/schemas/Hex" }, + "SuiChangeEpoch": { + "type": "object", + "required": [ + "computation_charge", + "epoch", + "storage_charge" + ], + "properties": { + "computation_charge": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epoch": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "storage_charge": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, "SuiJsonValue": {}, "TransactionBytes": { "type": "object", @@ -1873,6 +1898,19 @@ } }, "additionalProperties": false + }, + { + "description": "A system transaction that will update epoch information on-chain.", + "type": "object", + "required": [ + "ChangeEpoch" + ], + "properties": { + "ChangeEpoch": { + "$ref": "#/components/schemas/SuiChangeEpoch" + } + }, + "additionalProperties": false } ] }, diff --git a/crates/sui-types/src/crypto.rs b/crates/sui-types/src/crypto.rs index 51f67397e7d35..015e858c3eff2 100644 --- a/crates/sui-types/src/crypto.rs +++ b/crates/sui-types/src/crypto.rs @@ -273,6 +273,10 @@ impl Signature { secret.sign(&message) } + pub fn new_empty() -> Self { + Self([0u8; SUI_SIGNATURE_LENGTH]) + } + pub fn signature_bytes(&self) -> &[u8] { &self.0[..ed25519_dalek::SIGNATURE_LENGTH] } diff --git a/crates/sui-types/src/error.rs b/crates/sui-types/src/error.rs index 2e26b0de5ea30..2912c6b96a44d 100644 --- a/crates/sui-types/src/error.rs +++ b/crates/sui-types/src/error.rs @@ -118,6 +118,8 @@ pub enum SuiError { object_id: ObjectID, current_sequence_number: VersionNumber, }, + #[error("System Transaction not accepted")] + InvalidSystemTransaction, // Synchronization validation #[error("Transaction index must increase by one")] UnexpectedTransactionIndex, diff --git a/crates/sui-types/src/gas.rs b/crates/sui-types/src/gas.rs index 961fc595a9ba0..c945e660f8bd9 100644 --- a/crates/sui-types/src/gas.rs +++ b/crates/sui-types/src/gas.rs @@ -121,6 +121,7 @@ fn to_internal(external_units: GasUnits) -> InternalGasUnits { gas_status: GasStatus<'a>, init_budget: GasUnits, + charge: bool, computation_gas_unit_price: GasPrice, storage_gas_unit_price: GasPrice, /// storage_cost is the total storage gas units charged so far, due to writes into storage. @@ -142,13 +143,18 @@ impl<'a> SuiGasStatus<'a> { Self::new( GasStatus::new(&INITIAL_COST_SCHEDULE, GasUnits::new(gas_budget)), gas_budget, + true, computation_gas_unit_price, storage_gas_unit_price, ) } pub fn new_unmetered() -> SuiGasStatus<'a> { - Self::new(GasStatus::new_unmetered(), 0, 0, 0) + Self::new(GasStatus::new_unmetered(), 0, false, 0, 0) + } + + pub fn is_unmetered(&self) -> bool { + !self.charge } pub fn get_move_gas_status(&mut self) -> &mut GasStatus<'a> { @@ -183,6 +189,10 @@ impl<'a> SuiGasStatus<'a> { new_size: usize, storage_rebate: GasCarrier, ) -> SuiResult { + if self.is_unmetered() { + return Ok(0); + } + // Computation cost of a mutation is charged based on the sum of the old and new size. // This is because to update an object in the store, we have to erase the old one and // write a new one. @@ -234,12 +244,14 @@ impl<'a> SuiGasStatus<'a> { fn new( move_gas_status: GasStatus<'a>, gas_budget: u64, + charge: bool, computation_gas_unit_price: GasCarrier, storage_gas_unit_price: u64, ) -> SuiGasStatus<'a> { SuiGasStatus { gas_status: move_gas_status, init_budget: GasUnits::new(gas_budget), + charge, computation_gas_unit_price: GasPrice::new(computation_gas_unit_price), storage_gas_unit_price: GasPrice::new(storage_gas_unit_price), storage_cost: GasUnits::new(0), @@ -258,6 +270,9 @@ impl<'a> SuiGasStatus<'a> { } fn deduct_storage_cost(&mut self, cost: &StorageCost) -> SuiResult { + if self.is_unmetered() { + return Ok(0); + } let ext_cost = to_external(cost.0); let charge_amount = to_internal(ext_cost); let remaining_gas = self.gas_status.remaining_gas(); diff --git a/crates/sui-types/src/messages.rs b/crates/sui-types/src/messages.rs index f85d9cffa2c95..b5f4a1354668d 100644 --- a/crates/sui-types/src/messages.rs +++ b/crates/sui-types/src/messages.rs @@ -11,6 +11,7 @@ use crate::crypto::{ use crate::gas::GasCostSummary; use crate::messages_checkpoint::CheckpointFragment; use crate::object::{Object, ObjectFormatOptions, Owner, OBJECT_START_VERSION}; +use crate::SUI_SYSTEM_STATE_OBJECT_ID; use base64ct::Encoding; use itertools::Either; use move_binary_format::access::ModuleAccess; @@ -73,6 +74,16 @@ pub struct MoveModulePublish { pub modules: Vec>, } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct ChangeEpoch { + /// The next (to become) epoch ID. + pub epoch: EpochId, + /// The total amount of gas charged for staroge during the epoch. + pub storage_charge: u64, + /// The total amount of gas charged for computation during the epoch. + pub computation_charge: u64, +} + #[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] pub enum SingleTransactionKind { /// Initiate a coin transfer between addresses @@ -81,6 +92,15 @@ pub enum SingleTransactionKind { Publish(MoveModulePublish), /// Call a function in a published Move module Call(MoveCall), + /// A system transaction that will update epoch information on-chain. + /// It will only ever be executed once in an epoch. + /// The argument is the next epoch number, which is critical + /// because it ensures that this transaction has a unique digest. + /// This will eventually be translated to a Move call during execution. + /// It also doesn't require/use a gas object. + /// A validator will not sign a transaction of this kind from outside. It only + /// signs internally during epoch changes. + ChangeEpoch(ChangeEpoch), // .. more transaction types go here } @@ -141,6 +161,11 @@ impl SingleTransactionKind { .collect::>(); Transaction::input_objects_in_compiled_modules(&compiled_modules) } + Self::ChangeEpoch(_) => { + vec![InputObjectKind::SharedMoveObject( + SUI_SYSTEM_STATE_OBJECT_ID, + )] + } }; // Ensure that there are no duplicate inputs. This cannot be removed because: // In [`AuthorityState::check_locks`], we check that there are no duplicate mutable @@ -180,6 +205,12 @@ impl Display for SingleTransactionKind { writeln!(writer, "Arguments : {:?}", c.arguments)?; writeln!(writer, "Type Arguments : {:?}", c.type_arguments)?; } + Self::ChangeEpoch(e) => { + writeln!(writer, "Transaction Kind: Epoch Change")?; + writeln!(writer, "New epoch ID: {}", e.epoch)?; + writeln!(writer, "Storage gas reward: {}", e.storage_charge)?; + writeln!(writer, "Computation gas reward: {}", e.computation_charge)?; + } } write!(f, "{}", writer) } @@ -217,6 +248,13 @@ impl TransactionKind { TransactionKind::Batch(batch) => batch.len(), } } + + pub fn is_system_tx(&self) -> bool { + matches!( + self, + TransactionKind::Single(SingleTransactionKind::ChangeEpoch(_)) + ) + } } impl Display for TransactionKind { @@ -356,9 +394,11 @@ where result } }; - inputs.push(InputObjectKind::ImmOrOwnedMoveObject( - *self.gas_payment_object_ref(), - )); + if !self.kind.is_system_tx() { + inputs.push(InputObjectKind::ImmOrOwnedMoveObject( + *self.gas_payment_object_ref(), + )); + } Ok(inputs) } } @@ -392,14 +432,6 @@ pub struct TransactionEnvelope { impl TransactionEnvelope { pub fn verify_signature(&self) -> Result<(), SuiError> { - // We use this flag to see if someone has checked this before - // and therefore we can skip the check. Note that the flag has - // to be set to true manually, and is not set by calling this - // "check" function. - if self.is_verified { - return Ok(()); - } - let mut obligation = VerificationObligation::default(); self.add_tx_sig_to_verification_obligation(&mut obligation)?; obligation.verify_all().map(|_| ()) @@ -409,6 +441,14 @@ impl TransactionEnvelope { &self, obligation: &mut VerificationObligation, ) -> SuiResult<()> { + // We use this flag to see if someone has checked this before + // and therefore we can skip the check. Note that the flag has + // to be set to true manually, and is not set by calling this + // "check" function. + if self.is_verified || self.data.kind.is_system_tx() { + return Ok(()); + } + let (message, signature, public_key) = self .tx_signature .get_verification_inputs(&self.data, self.data.sender)?; @@ -451,11 +491,11 @@ impl TransactionEnvelope { pub fn input_objects_in_compiled_modules( compiled_modules: &[CompiledModule], ) -> Vec { - let to_be_publised: BTreeSet<_> = compiled_modules.iter().map(|m| m.self_id()).collect(); + let to_be_published: BTreeSet<_> = compiled_modules.iter().map(|m| m.self_id()).collect(); let mut dependent_packages = BTreeSet::new(); for module in compiled_modules { for handle in &module.module_handles { - if !to_be_publised.contains(&module.module_id_for_handle(handle)) { + if !to_be_published.contains(&module.module_id_for_handle(handle)) { let address = ObjectID::from(*module.address_identifier_at(handle.address)); dependent_packages.insert(address); } @@ -567,6 +607,39 @@ impl SignedTransaction { } } + pub fn new_change_epoch( + cur_epoch: EpochId, + storage_charge: u64, + computation_charge: u64, + authority: AuthorityName, + secret: &dyn signature::Signer, + ) -> Self { + let kind = TransactionKind::Single(SingleTransactionKind::ChangeEpoch(ChangeEpoch { + epoch: cur_epoch + 1, + storage_charge, + computation_charge, + })); + // For the ChangeEpoch transaction, we do not care about the sender and the gas. + let data = TransactionData::new( + kind, + SuiAddress::default(), + (ObjectID::ZERO, SequenceNumber::default(), ObjectDigest::MIN), + 0, + ); + let signature = AuthoritySignature::new(&data, secret); + Self { + transaction_digest: OnceCell::new(), + is_verified: false, + data, + tx_signature: Signature::new_empty(), + auth_sign_info: AuthoritySignInfo { + epoch: cur_epoch, + authority, + signature, + }, + } + } + /// Verify the signature and return the non-zero voting right of the authority. pub fn verify(&self, committee: &Committee) -> Result { self.verify_signature()?; diff --git a/crates/sui-types/src/sui_system_state.rs b/crates/sui-types/src/sui_system_state.rs index 8f1a332123d69..74c0ec39c0257 100644 --- a/crates/sui-types/src/sui_system_state.rs +++ b/crates/sui-types/src/sui_system_state.rs @@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize}; use crate::{balance::Balance, coin::TreasuryCap, id::VersionedID, SUI_FRAMEWORK_ADDRESS}; const SUI_SYSTEM_STATE_STRUCT_NAME: &IdentStr = ident_str!("SuiSystemState"); -const SUI_SYSTEM_MODULE_NAME: &IdentStr = ident_str!("SuiSystem"); +pub const SUI_SYSTEM_MODULE_NAME: &IdentStr = ident_str!("SuiSystem"); +pub const ADVANCE_EPOCH_FUNCTION_NAME: &IdentStr = ident_str!("advance_epoch"); /// Rust version of the Move Sui::SuiSystem::SystemParameters type #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]