Skip to content

Commit 821de7b

Browse files
authored
feat(authority): support failing validation effects commit post-consensus (#8927)
# Description of change This makes so that a move authentication passing pre-consensus (validators sign the TX) and failing post-consensus produces some effects (gas charging). I think starting from `test_abstract_account_post_consensus_failure()` is a good way to understand the logic behind the change. Basically, what is needed, is to make it so that this can happen: 1) Create an AA TX and make the majority of validators to sign it; this means that the Move authentication based on the AA shared object state is CURRENTLY successful. 2) Then, tamper with the AA shared object state by altering the state of the AA shared object (e.g., create and send for execution a second TX that changes a field of the AA shared object). 3) Finally, submit for execution the original certificate, i.e., the one obtained from the signature of the majority of validators. This PR changes the behavior at this step: - before this PR, the TX execution simply didn't go through but just raised a Move authentication error within the iota-core; - whilst, after this PR, the Move authentication error is treated as execution error and thus producing effects; the result is that all input objects are considered when creating the transaction effects and the gas coin is reduced with the authentication computation gas cost execution. This PR also merges input objects in the case of a successful execution. In order to produce effects, the method `authenticate_then_execute_transaction_to_effects()` was added to the execution engine. This new method executes all the steps necessary to create the TransactionEffects after the authentication execution. This means that, after running `authenticate_transaction_inner()` and failing (as well as succeeding), the execution continues with `execute_transaction_to_effects_inner()` in order to finish the job, i.e., creating effects for a failing (or successful) transaction. ## Links to any relevant issues Fixes #8908 ## How the change has been tested - [x] Basic tests (linting, compilation, formatting, unit/integration tests) - [x] Patch-specific tests (correctness, functionality coverage) - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have checked that new and existing unit tests pass locally with my changes
1 parent ccd054e commit 821de7b

File tree

16 files changed

+1004
-492
lines changed

16 files changed

+1004
-492
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/iota-core/src/authority.rs

Lines changed: 72 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ impl AuthorityState {
913913
)?;
914914

915915
if let Some(move_authenticator) = transaction.sender_move_authenticator() {
916-
// It is supposed that `Move authentication` availability is checked in
916+
// It is supposed that `MoveAuthenticator` availability is checked in
917917
// `SenderSignedData::validity_check`.
918918

919919
// Check the `MoveAuthenticator` input objects.
@@ -934,7 +934,7 @@ impl AuthorityState {
934934
let (kind, signer, _) = tx_data.execution_parts();
935935

936936
// Execute the Move authenticator.
937-
let validation_result = epoch_store.executor().validate_transaction(
937+
let validation_result = epoch_store.executor().authenticate_transaction(
938938
self.get_backing_store().as_ref(),
939939
protocol_config,
940940
self.metrics.limits_metrics.clone(),
@@ -1349,7 +1349,7 @@ impl AuthorityState {
13491349
.start_timer();
13501350

13511351
let objects = if let Some(move_authenticator) = certificate.sender_move_authenticator() {
1352-
// It is supposed that `Move authentication` availability is checked in
1352+
// It is supposed that `MoveAuthenticator` availability is checked in
13531353
// `SenderSignedData::validity_check`.
13541354

13551355
// Load the account object.
@@ -1746,7 +1746,7 @@ impl AuthorityState {
17461746
certificate: &VerifiedExecutableTransaction,
17471747
tx_input_objects: InputObjects,
17481748
account_object: Option<ObjectReadResult>,
1749-
authenticator_input_objects: Option<InputObjects>,
1749+
move_authenticator_input_objects: Option<InputObjects>,
17501750
epoch_store: &Arc<AuthorityPerEpochStore>,
17511751
) -> IotaResult<(
17521752
InnerTemporaryStore,
@@ -1778,10 +1778,15 @@ impl AuthorityState {
17781778

17791779
let (kind, signer, gas) = tx_data.execution_parts();
17801780

1781-
let authenticator_computation_cost = if let Some(move_authenticator) =
1781+
#[cfg_attr(not(any(msim, fail_points)), expect(unused_mut))]
1782+
let (inner_temp_store, _, mut effects, execution_error_opt) = if let Some(
1783+
move_authenticator,
1784+
) =
17821785
certificate.sender_move_authenticator()
17831786
{
1784-
// It is supposed that `Move authentication` availability is checked in
1787+
// Check if the sender needs to be authenticated in Move and, if so, execute the
1788+
// authentication.
1789+
// It is supposed that `MoveAuthenticator` availability is checked in
17851790
// `SenderSignedData::validity_check`.
17861791

17871792
// Check basic `object_to_authenticate` preconditions and get its components.
@@ -1803,71 +1808,71 @@ impl AuthorityState {
18031808
&signer,
18041809
)?;
18051810

1806-
let authenticator_input_objects = authenticator_input_objects.expect(
1807-
"In case of a `MoveAuthenticator` signature, the authenticator input objects must be provided",
1808-
);
1811+
let move_authenticator_input_objects = move_authenticator_input_objects.expect(
1812+
"In case of a `MoveAuthenticator` signature, the authenticator input objects must be provided",
1813+
);
18091814

18101815
// Check the `MoveAuthenticator` input objects.
1811-
let (authenticator_gas_status, authenticator_input_objects) =
1812-
Self::check_move_authenticator_inputs_for_executing(
1813-
protocol_config,
1814-
reference_gas_price,
1815-
tx_data,
1816-
authenticator_input_objects,
1817-
&tx_input_objects,
1818-
)?;
1819-
1820-
// Execute the Move authenticator.
1821-
let validation_result = epoch_store.executor().validate_transaction(
1822-
backing_store,
1816+
// The `MoveAuthenticator` receiving objects are checked on the signing step.
1817+
// `max_auth_gas` is used here as a Move authenticator gas budget until it is
1818+
// not a part of the transaction data.
1819+
let authenticator_gas_budget = protocol_config.max_auth_gas();
1820+
let (
1821+
gas_status,
1822+
authenticator_checked_input_objects,
1823+
authenticator_and_tx_checked_input_objects,
1824+
) = iota_transaction_checks::check_certificate_and_move_authenticator_input(
1825+
certificate,
1826+
tx_input_objects,
1827+
move_authenticator_input_objects,
1828+
authenticator_gas_budget,
18231829
protocol_config,
1824-
self.metrics.limits_metrics.clone(),
1825-
&epoch_id,
1826-
epoch_start_timestamp,
1827-
authenticator_gas_status,
1828-
move_authenticator.to_owned(),
1829-
authenticator_info,
1830-
authenticator_input_objects,
1831-
kind.clone(),
1832-
signer,
1833-
tx_digest,
1834-
&mut None,
1835-
);
1830+
reference_gas_price,
1831+
)?;
18361832

1837-
match validation_result {
1838-
Ok(authenticator_computation_cost) => authenticator_computation_cost,
1839-
Err(validation_error) => {
1840-
// If an error occurs during execution, the storage, effects, and the execution
1841-
// error itself are returned so that the gas can be charged.
1842-
// I case of validation we do not support this, so the only available option
1843-
// here is to return an error.
1844-
return Err(IotaError::MoveAuthenticatorExecutionFailure {
1845-
error: validation_error.to_string(),
1846-
});
1847-
}
1848-
}
1849-
} else {
1850-
0
1851-
};
1833+
let owned_object_refs = authenticator_and_tx_checked_input_objects
1834+
.inner()
1835+
.filter_owned_objects();
1836+
self.check_owned_locks(&owned_object_refs)?;
18521837

1853-
// The cost of partially re-auditing a transaction before execution is
1854-
// tolerated.
1855-
//
1856-
// TODO: Gas coins should not be double-checked if `GenericSignature` is of
1857-
// type `MoveAuthenticator`.
1858-
let (tx_gas_status, tx_input_objects) = iota_transaction_checks::check_certificate_input(
1859-
certificate,
1860-
tx_input_objects,
1861-
protocol_config,
1862-
reference_gas_price,
1863-
authenticator_computation_cost,
1864-
)?;
1838+
epoch_store
1839+
.executor()
1840+
.authenticate_then_execute_transaction_to_effects(
1841+
backing_store,
1842+
protocol_config,
1843+
self.metrics.limits_metrics.clone(),
1844+
self.config
1845+
.expensive_safety_check_config
1846+
.enable_deep_per_tx_iota_conservation_check(),
1847+
self.config.certificate_deny_config.certificate_deny_set(),
1848+
&epoch_id,
1849+
epoch_start_timestamp,
1850+
gas_status,
1851+
gas,
1852+
move_authenticator.to_owned(),
1853+
authenticator_info,
1854+
authenticator_checked_input_objects,
1855+
authenticator_and_tx_checked_input_objects,
1856+
kind,
1857+
signer,
1858+
tx_digest,
1859+
&mut None,
1860+
)
1861+
} else {
1862+
// No Move authentication required, proceed to execute the transaction directly.
18651863

1866-
let owned_object_refs = tx_input_objects.inner().filter_owned_objects();
1867-
self.check_owned_locks(&owned_object_refs)?;
1864+
// The cost of partially re-auditing a transaction before execution is
1865+
// tolerated.
1866+
let (tx_gas_status, tx_checked_input_objects) =
1867+
iota_transaction_checks::check_certificate_input(
1868+
certificate,
1869+
tx_input_objects,
1870+
protocol_config,
1871+
reference_gas_price,
1872+
)?;
18681873

1869-
#[cfg_attr(not(any(msim, fail_points)), expect(unused_mut))]
1870-
let (inner_temp_store, _, mut effects, execution_error_opt) =
1874+
let owned_object_refs = tx_checked_input_objects.inner().filter_owned_objects();
1875+
self.check_owned_locks(&owned_object_refs)?;
18711876
epoch_store.executor().execute_transaction_to_effects(
18721877
backing_store,
18731878
protocol_config,
@@ -1880,14 +1885,15 @@ impl AuthorityState {
18801885
self.config.certificate_deny_config.certificate_deny_set(),
18811886
&epoch_id,
18821887
epoch_start_timestamp,
1883-
tx_input_objects,
1888+
tx_checked_input_objects,
18841889
gas,
18851890
tx_gas_status,
18861891
kind,
18871892
signer,
18881893
tx_digest,
18891894
&mut None,
1890-
);
1895+
)
1896+
};
18911897

18921898
fail_point_if!("cp_execution_nondeterminism", || {
18931899
#[cfg(msim)]
@@ -5607,32 +5613,6 @@ impl AuthorityState {
56075613
Ok((gas_status, checked_input_objects, authenticator_info))
56085614
}
56095615

5610-
/// Checks the `MoveAuthenticator` inputs for executing.
5611-
fn check_move_authenticator_inputs_for_executing(
5612-
protocol_config: &ProtocolConfig,
5613-
reference_gas_price: u64,
5614-
transaction: &TransactionData,
5615-
authenticator_input_objects: InputObjects,
5616-
tx_input_objects: &InputObjects,
5617-
) -> IotaResult<(IotaGasStatus, CheckedInputObjects)> {
5618-
// The `MoveAuthenticator` receiving objects are checked on the signing step.
5619-
5620-
// `max_auth_gas` is used here as a Move authenticator gas budget until it is
5621-
// not a part of the transaction data.
5622-
let authenticator_gas_budget = protocol_config.max_auth_gas();
5623-
5624-
iota_transaction_checks::check_move_authenticator_input(
5625-
protocol_config,
5626-
reference_gas_price,
5627-
transaction.gas(),
5628-
authenticator_gas_budget,
5629-
transaction.gas_budget(),
5630-
transaction.gas_price(),
5631-
authenticator_input_objects,
5632-
tx_input_objects,
5633-
)
5634-
}
5635-
56365616
#[cfg(test)]
56375617
pub(crate) fn iter_live_object_set_for_testing(
56385618
&self,

crates/iota-e2e-tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ iota-test-transaction-builder.workspace = true
6464
iota-tool.workspace = true
6565
iota-types.workspace = true
6666
move-binary-format.workspace = true
67+
move-command-line-common.workspace = true
6768
move-core-types.workspace = true
6869
move-package.workspace = true
6970
shared-crypto.workspace = true

0 commit comments

Comments
 (0)