Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f071757
[vm] New VM instruction for aborting with message
calintat Dec 9, 2025
61f3fae
Merge branch 'main' into calin/vm-abort-message
calintat Dec 15, 2025
632f176
Fix hashing
calintat Dec 16, 2025
24b0e46
Add type checks for Abort/AbortMsg
calintat Dec 16, 2025
c14d6cb
Add comment
calintat Dec 16, 2025
02904e7
Merge branch 'main' into calin/vm-abort-message
calintat Dec 16, 2025
5cfee84
Do not change `ExecutionStatus`
calintat Dec 16, 2025
63d94a9
Merge branch 'main' into calin/vm-abort-message
calintat Dec 16, 2025
4f9bfb3
Revert changes
calintat Dec 16, 2025
86045a6
Cursor feedback
calintat Dec 16, 2025
0befc1d
Merge branch 'main' into calin/vm-abort-message
calintat Dec 17, 2025
2998bb5
Fix formatting
calintat Dec 18, 2025
052585f
Fix macro
calintat Dec 18, 2025
4071ee7
Return error on message type mismatch
calintat Dec 18, 2025
57a3903
Include abort message in prologue/epilogue for debugging
calintat Dec 18, 2025
1af6390
[vm] New bytecode for aborting with message
calintat Dec 18, 2025
ad54d9f
Cursor feedback
calintat Dec 19, 2025
062615e
Rustfmt
calintat Dec 22, 2025
28128aa
More cursor feedback
calintat Dec 22, 2025
f3437ed
More places to handle `AbortMsg` variant
calintat Dec 22, 2025
a7c3e59
Merge branch 'main' into calin/vm-abort-message
calintat Dec 22, 2025
f35c1b7
Merge branch 'calin/vm-abort-message' into calin/vm-abort-message-2
calintat Dec 22, 2025
cb3601b
Feature gate abort messages
calintat Dec 22, 2025
0629b08
Merge branch 'calin/vm-abort-message' into calin/vm-abort-message-2
calintat Dec 22, 2025
aceb1f6
Use feature flag to enable abort messages in VM
calintat Dec 22, 2025
1aeeb54
Add comment
calintat Dec 22, 2025
dba84cd
Replace `&vector<u8>` with `vector<u8>`
calintat Dec 22, 2025
f311fb2
Merge branch 'calin/vm-abort-message' into calin/vm-abort-message-2
calintat Dec 22, 2025
572af72
Replace `&vector<u8>` with `vector<u8>`
calintat Dec 22, 2025
ee2a504
Merge branch 'main' into calin/vm-abort-message
calintat Dec 23, 2025
3b919c3
Fix error for invalid abort messages
calintat Dec 23, 2025
2109790
Merge branch 'calin/vm-abort-message' into calin/vm-abort-message-2
calintat Dec 23, 2025
2d7df56
Merge branch 'main' into calin/vm-abort-message-2
calintat Dec 29, 2025
4b4ed18
Merge branch 'main' into calin/vm-abort-message-2
calintat Dec 30, 2025
37b17cd
Combine `Abort` and `AbortMsg` in stackless buytecode
calintat Dec 30, 2025
f78fb2f
Wolfgang feedback
calintat Dec 30, 2025
709dede
George feedback
calintat Dec 30, 2025
289bf35
Add test for invalid message
calintat Dec 30, 2025
4ddc0fb
Charge gas per byte
calintat Dec 30, 2025
40c8528
Clippy
calintat Dec 30, 2025
7063878
Merge branch 'main' into calin/vm-abort-message-2
calintat Dec 30, 2025
7c8cc41
Add to default features
calintat Jan 2, 2026
cfae669
Merge branch 'main' into calin/vm-abort-message-2
calintat Jan 2, 2026
26d4771
Update tests
calintat Jan 2, 2026
ebfaca7
Fix gas profiler
calintat Jan 2, 2026
50e630f
Simplify gas model
calintat Jan 2, 2026
62a093e
Fix sourcifier
calintat Jan 5, 2026
830d2df
Cap length of abort message
calintat Jan 6, 2026
a1a59c2
Update limit
calintat Jan 6, 2026
907cb44
Update abort_msg_per_byte
calintat Jan 7, 2026
df32ae4
Merge branch 'main' into calin/vm-abort-message-2
calintat Jan 7, 2026
b287a30
Merge branch 'main' into calin/vm-abort-message-2
calintat Jan 8, 2026
cd2c1c1
Feature gate new gas params
calintat Jan 9, 2026
fa4225f
Merge branch 'main' into calin/vm-abort-message-2
calintat Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions aptos-move/aptos-gas-meter/src/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ where

self.algebra.charge_execution(cost)
}

fn charge_abort_message(&mut self, bytes: &[u8]) -> PartialVMResult<()> {
let num_bytes = NumBytes::new(bytes.len() as u64);
let cost = ABORT_MSG_BASE + ABORT_MSG_PER_BYTE * num_bytes;
self.algebra.charge_execution(cost)
}
}

impl<A> AptosGasMeter for StandardGasMeter<A>
Expand Down
3 changes: 3 additions & 0 deletions aptos-move/aptos-gas-profiling/src/profiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ where

[VEC_SWAP]
fn charge_vec_swap(&mut self) -> PartialVMResult<()>;

[ABORT_MSG]
fn charge_abort_message(&mut self, bytes: &[u8]) -> PartialVMResult<()>;
}

fn balance_internal(&self) -> InternalGas {
Expand Down
4 changes: 3 additions & 1 deletion aptos-move/aptos-gas-schedule/src/gas_schedule/instr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! This module defines the gas parameters for all Move instructions.

use crate::{
gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_33, RELEASE_V1_38},
gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_33, RELEASE_V1_38, RELEASE_V1_40},
gas_schedule::VMGasParameters,
};
use aptos_gas_algebra::{
Expand All @@ -22,6 +22,8 @@ crate::gas_schedule::macros::define_gas_parameters!(
// control flow
[ret: InternalGas, "ret", 220],
[abort: InternalGas, "abort", 220],
[abort_msg_base: InternalGas, { RELEASE_V1_40.. => "abort_msg.base" }, 440],
[abort_msg_per_byte: InternalGasPerByte, { RELEASE_V1_40.. => "abort_msg.per_byte" }, 45],

// Note(Gas): The costs of the branch instructions have been jacked up a bit intentionally
// to prevent any single transaction from running for too long.
Expand Down
11 changes: 11 additions & 0 deletions aptos-move/aptos-memory-usage-tracker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,17 @@ where
.charge_native_function_before_execution(ty_args, args)
}

fn charge_abort_message(&mut self, bytes: &[u8]) -> PartialVMResult<()> {
self.release_heap_memory(
self.vm_gas_params()
.misc
.abs_val
.abstract_heap_size(bytes, self.feature_version())?,
);

self.base.charge_abort_message(bytes)
}

#[inline]
fn charge_native_function(
&mut self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ pub enum FeatureFlag {
EnableFrameworkForOption,
SessionContinuation,
EnableFunctionReflection,
VMBinaryFormatV10,
}

fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
Expand Down Expand Up @@ -413,6 +414,7 @@ impl From<FeatureFlag> for AptosFeatureFlag {
FeatureFlag::EnableFrameworkForOption => AptosFeatureFlag::ENABLE_FRAMEWORK_FOR_OPTION,
FeatureFlag::SessionContinuation => AptosFeatureFlag::SESSION_CONTINUATION,
FeatureFlag::EnableFunctionReflection => AptosFeatureFlag::ENABLE_FUNCTION_REFLECTION,
FeatureFlag::VMBinaryFormatV10 => AptosFeatureFlag::VM_BINARY_FORMAT_V10,
}
}
}
Expand Down Expand Up @@ -596,6 +598,7 @@ impl From<AptosFeatureFlag> for FeatureFlag {
AptosFeatureFlag::ENABLE_FRAMEWORK_FOR_OPTION => FeatureFlag::EnableFrameworkForOption,
AptosFeatureFlag::SESSION_CONTINUATION => FeatureFlag::SessionContinuation,
AptosFeatureFlag::ENABLE_FUNCTION_REFLECTION => FeatureFlag::EnableFunctionReflection,
AptosFeatureFlag::VM_BINARY_FORMAT_V10 => FeatureFlag::VMBinaryFormatV10,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
processed 4 tasks
task 1 lines 4-35: print-bytecode --input=module [module Alice::game {]
// Bytecode version v9
// Bytecode version v10
module 0xf75daa73fc071f93593335eb9033da804777eb94491650dd3f095ce6f778acb6::game
use 0x1::signer
use 0x1::debug
Expand Down
1 change: 1 addition & 0 deletions aptos-move/aptos-vm/src/verifier/event_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ pub(crate) fn validate_emit_calls(
| Le
| Ge
| Abort
| AbortMsg
| Nop
| ImmBorrowVariantField(_)
| ImmBorrowVariantFieldGeneric(_)
Expand Down
4 changes: 2 additions & 2 deletions third_party/move/move-binary-format/src/check_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,8 +673,8 @@ impl<'a> BoundsChecker<'a> {
| LdI8(_) | LdI16(_) | LdI32(_) | LdI64(_) | LdI256(_) | LdI128(_) | CastI8
| CastI16 | CastI32 | CastI64 | CastI128 | CastI256 | LdTrue | LdFalse
| ReadRef | WriteRef | Add | Sub | Mul | Mod | Div | Negate | BitOr | BitAnd
| Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | Abort | Nop => {
},
| Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | Abort
| AbortMsg | Nop => {},
}
}
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ impl BinaryComplexityMeter<'_> {
| MoveTo(_)
| MoveFrom(_)
| Abort
| AbortMsg
| Nop => (),
}
}
Expand Down
11 changes: 11 additions & 0 deletions third_party/move/move-binary-format/src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,14 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec<Bytecode>) -> BinaryLo
)),
);
},
Opcodes::ABORT_MSG if cursor.version() < VERSION_10 => {
return Err(
PartialVMError::new(StatusCode::MALFORMED).with_message(format!(
"aborting with message not available before bytecode version {}",
VERSION_10
)),
);
},
_ => {},
};

Expand Down Expand Up @@ -1943,6 +1951,7 @@ fn load_code(cursor: &mut VersionedCursor, code: &mut Vec<Bytecode>) -> BinaryLo
Opcodes::LE => Bytecode::Le,
Opcodes::GE => Bytecode::Ge,
Opcodes::ABORT => Bytecode::Abort,
Opcodes::ABORT_MSG => Bytecode::AbortMsg,
Opcodes::NOP => Bytecode::Nop,
Opcodes::EXISTS => Bytecode::Exists(load_struct_def_index(cursor)?),
Opcodes::EXISTS_GENERIC => Bytecode::ExistsGeneric(load_struct_def_inst_index(cursor)?),
Expand Down Expand Up @@ -2226,6 +2235,8 @@ impl Opcodes {
0x65 => Ok(Opcodes::CAST_I128),
0x66 => Ok(Opcodes::CAST_I256),
0x67 => Ok(Opcodes::NEGATE),
// Since bytecode version 10
0x68 => Ok(Opcodes::ABORT_MSG),
_ => Err(PartialVMError::new(StatusCode::UNKNOWN_OPCODE)
.with_message(format!("code {:X}", value))),
}
Expand Down
22 changes: 21 additions & 1 deletion third_party/move/move-binary-format/src/file_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3050,6 +3050,21 @@ pub enum Bytecode {
ty_stack << ty
"#]
Negate,

#[group = "control_flow"]
#[description = r#"
Abort the transaction with an error code and message.
"#]
#[semantics = r#"
stack >> (error_message: vector<u8>)
stack >> (error_code: u64)
abort transaction with error_code and error_message
"#]
#[runtime_check_prologue = r#"
ty_stack >> _
ty_stack >> _
"#]
AbortMsg,
}

impl ::std::fmt::Debug for Bytecode {
Expand Down Expand Up @@ -3149,6 +3164,7 @@ impl ::std::fmt::Debug for Bytecode {
Bytecode::Le => write!(f, "Le"),
Bytecode::Ge => write!(f, "Ge"),
Bytecode::Abort => write!(f, "Abort"),
Bytecode::AbortMsg => write!(f, "AbortMsg"),
Bytecode::Nop => write!(f, "Nop"),
Bytecode::Exists(a) => write!(f, "Exists({:?})", a),
Bytecode::ExistsGeneric(a) => write!(f, "ExistsGeneric({:?})", a),
Expand All @@ -3171,7 +3187,10 @@ impl ::std::fmt::Debug for Bytecode {
impl Bytecode {
/// Return true if this bytecode instruction always branches
pub fn is_unconditional_branch(&self) -> bool {
matches!(self, Bytecode::Ret | Bytecode::Abort | Bytecode::Branch(_))
matches!(
self,
Bytecode::Ret | Bytecode::Abort | Bytecode::AbortMsg | Bytecode::Branch(_)
)
}

/// Return true if the branching behavior of this bytecode instruction depends on a runtime
Expand Down Expand Up @@ -3328,6 +3347,7 @@ impl Bytecode {
| Le
| Ge
| Abort
| AbortMsg
| Nop
| ImmBorrowVariantField(_)
| ImmBorrowVariantFieldGeneric(_)
Expand Down
11 changes: 9 additions & 2 deletions third_party/move/move-binary-format/src/file_format_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,9 @@ pub enum Opcodes {
CAST_I64 = 0x64,
CAST_I128 = 0x65,
CAST_I256 = 0x66,
NEGATE = 0x67
NEGATE = 0x67,
// Since bytecode version 10
ABORT_MSG = 0x68,
}

/// Upper limit on the binary size
Expand Down Expand Up @@ -552,11 +554,15 @@ pub const VERSION_8: u32 = 8;
/// + allow `$` in identifiers
pub const VERSION_9: u32 = 9;

/// Version 10: changes compared to version 9
/// + abort with message instruction
pub const VERSION_10: u32 = 10;

/// Mark which oldest version is supported.
pub const VERSION_MIN: u32 = VERSION_5;

/// Mark which version is the latest version.
pub const VERSION_MAX: u32 = VERSION_9;
pub const VERSION_MAX: u32 = VERSION_10;

/// Mark which version is the default version. This is the version used by default by tools like
/// the compiler. Notice that this version might be different from the one supported on nodes.
Expand Down Expand Up @@ -797,6 +803,7 @@ pub fn instruction_key(instruction: &Bytecode) -> u8 {
Le => Opcodes::LE,
Ge => Opcodes::GE,
Abort => Opcodes::ABORT,
AbortMsg => Opcodes::ABORT_MSG,
Nop => Opcodes::NOP,
Exists(_) => Opcodes::EXISTS,
ExistsGeneric(_) => Opcodes::EXISTS_GENERIC,
Expand Down
1 change: 1 addition & 0 deletions third_party/move/move-binary-format/src/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,7 @@ fn serialize_instruction_inner(
Bytecode::Le => binary.push(Opcodes::LE as u8),
Bytecode::Ge => binary.push(Opcodes::GE as u8),
Bytecode::Abort => binary.push(Opcodes::ABORT as u8),
Bytecode::AbortMsg => binary.push(Opcodes::ABORT_MSG as u8),
Bytecode::Nop => binary.push(Opcodes::NOP as u8),
Bytecode::Exists(class_idx) => {
binary.push(Opcodes::EXISTS as u8)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ impl<'a> ApplyCodeUnitBoundsContext<'a> {
| LdI256(_) | CastI8 | CastI16 | CastI32 | CastI64 | CastI128 | CastI256
| LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub | Mul | Mod | Div
| Negate | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq | Neq
| Lt | Gt | Le | Ge | Abort | Nop => {
| Lt | Gt | Le | Ge | Abort | AbortMsg | Nop => {
panic!("Bytecode has no internal index: {:?}", code[bytecode_idx])
},
PackVariant(_)
Expand Down Expand Up @@ -563,7 +563,7 @@ fn is_interesting(bytecode: &Bytecode) -> bool {
| LdI16(_) | LdI32(_) | LdI64(_) | LdI128(_) | LdI256(_) | CastI8 | CastI16 | CastI32
| CastI64 | CastI128 | CastI256 | LdTrue | LdFalse | ReadRef | WriteRef | Add | Sub
| Mul | Mod | Div | Negate | BitOr | BitAnd | Xor | Shl | Shr | Or | And | Not | Eq
| Neq | Lt | Gt | Le | Ge | Abort | Nop => false,
| Neq | Lt | Gt | Le | Ge | Abort | AbortMsg | Nop => false,

PackClosure(..)
| PackClosureGeneric(..)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl<'a> AcquiresVerifier<'a> {
| Bytecode::BrTrue(_)
| Bytecode::BrFalse(_)
| Bytecode::Abort
| Bytecode::AbortMsg
| Bytecode::Branch(_)
| Bytecode::Nop
| Bytecode::Ret
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ impl<'a> InstantiationLoopChecker<'a> {
| Bytecode::Le
| Bytecode::Ge
| Bytecode::Abort
| Bytecode::AbortMsg
| Bytecode::Nop
| Bytecode::Exists(_)
| Bytecode::ExistsGeneric(_)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ impl<'a> InstructionConsistency<'a> {
| Xor | Shl | Shr | Or | And | Not | Eq | Neq | Lt | Gt | Le | Ge | CopyLoc(_)
| MoveLoc(_) | StLoc(_) | MutBorrowLoc(_) | ImmBorrowLoc(_) | VecLen(_)
| VecImmBorrow(_) | VecMutBorrow(_) | VecPushBack(_) | VecPopBack(_)
| VecSwap(_) | Abort | Nop | LdI8(_) | LdI16(_) | LdI32(_) | LdI64(_)
| LdI128(_) | LdI256(_) | CastI8 | CastI16 | CastI32 | CastI64 | CastI128
| CastI256 => (),
| VecSwap(_) | Abort | AbortMsg | Nop | LdI8(_) | LdI16(_) | LdI32(_)
| LdI64(_) | LdI128(_) | LdI256(_) | CastI8 | CastI16 | CastI32 | CastI64
| CastI128 | CastI256 => (),
}
}
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fn execute_inner(
| Bytecode::BrTrue(_)
| Bytecode::BrFalse(_)
| Bytecode::Abort
| Bytecode::AbortMsg
| Bytecode::Branch(_)
| Bytecode::Nop
| Bytecode::FreezeRef
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,12 @@ fn execute_inner(
Bytecode::BrTrue(_) | Bytecode::BrFalse(_) | Bytecode::Abort => {
safe_assert!(safe_unwrap!(verifier.stack.pop()).is_value());
},
Bytecode::AbortMsg => {
// message value
safe_assert!(safe_unwrap!(verifier.stack.pop()).is_value());
// code value
safe_assert!(safe_unwrap!(verifier.stack.pop()).is_value());
},
Bytecode::MoveTo(_) | Bytecode::MoveToGeneric(_) => {
// resource value
safe_assert!(safe_unwrap!(verifier.stack.pop()).is_value());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,7 @@ impl<'a, const N: usize> SignatureChecker<'a, N> {
| MoveTo(_)
| MoveFrom(_)
| Abort
| AbortMsg
| Nop => (),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,12 @@ impl<'a> StackUsageVerifier<'a> {
// Vector indexing operations (pop twice and push once)
Bytecode::VecImmBorrow(_) | Bytecode::VecMutBorrow(_) => (2, 1),

// MoveTo, WriteRef, and VecPushBack pop twice but do not push
// MoveTo, WriteRef, VecPushBack, and AbortMsg pop twice but do not push
Bytecode::MoveTo(_)
| Bytecode::MoveToGeneric(_)
| Bytecode::WriteRef
| Bytecode::VecPushBack(_) => (2, 0),
| Bytecode::VecPushBack(_)
| Bytecode::AbortMsg => (2, 0),

// VecSwap pops three times but does not push
Bytecode::VecSwap(_) => (3, 0),
Expand Down
Loading
Loading