Make pyspec disable withdrawal-functions in EIP4844

This commit is contained in:
Hsiao-Wei Wang 2022-10-26 10:53:04 -05:00
parent ca538f52a0
commit 317209591d
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
9 changed files with 106 additions and 72 deletions

View File

@ -617,6 +617,34 @@ KZG_SETUP_LAGRANGE = TESTING_KZG_SETUP_LAGRANGE
ROOTS_OF_UNITY = kzg.compute_roots_of_unity(TESTING_FIELD_ELEMENTS_PER_BLOB)
#
# Temporarily disable Withdrawals functions for EIP4844 testnets
#
def no_op(fn): # type: ignore
def wrapper(*args, **kw): # type: ignore
return None
return wrapper
def get_empty_list_result(fn): # type: ignore
def wrapper(*args, **kw): # type: ignore
return []
return wrapper
process_full_withdrawals = no_op(process_full_withdrawals)
process_partial_withdrawals = no_op(process_partial_withdrawals)
process_withdrawals = no_op(process_withdrawals)
process_bls_to_execution_change = no_op(process_bls_to_execution_change)
get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals)
#
# End
#
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> BlobsSidecar:
pass'''

View File

@ -1,7 +1,8 @@
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later, always_bls
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_phases, always_bls
def run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=True):
@ -37,14 +38,14 @@ def run_bls_to_execution_change_processing(spec, state, signed_address_change, v
yield 'post', state
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success(spec, state):
signed_address_change = get_signed_address_change(spec, state)
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_not_activated(spec, state):
validator_index = 3
@ -62,7 +63,7 @@ def test_success_not_activated(spec, state):
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_in_activation_queue(spec, state):
validator_index = 3
@ -80,7 +81,7 @@ def test_success_in_activation_queue(spec, state):
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_in_exit_queue(spec, state):
validator_index = 3
@ -93,7 +94,7 @@ def test_success_in_exit_queue(spec, state):
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_exited(spec, state):
validator_index = 4
@ -110,7 +111,7 @@ def test_success_exited(spec, state):
assert not spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_withdrawable(spec, state):
validator_index = 4
@ -128,7 +129,7 @@ def test_success_withdrawable(spec, state):
assert spec.is_fully_withdrawable_validator(validator, balance, spec.get_current_epoch(state))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_val_index_out_of_range(spec, state):
# Create for one validator beyond the validator list length
@ -137,7 +138,7 @@ def test_fail_val_index_out_of_range(spec, state):
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_already_0x01(spec, state):
# Create for one validator beyond the validator list length
@ -149,7 +150,7 @@ def test_fail_already_0x01(spec, state):
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_incorrect_from_bls_pubkey(spec, state):
# Create for one validator beyond the validator list length
@ -163,7 +164,7 @@ def test_fail_incorrect_from_bls_pubkey(spec, state):
yield from run_bls_to_execution_change_processing(spec, state, signed_address_change, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
@always_bls
def test_fail_bad_signature(spec, state):

View File

@ -1,7 +1,8 @@
from eth2spec.test.context import (
spec_state_test,
with_capella_and_later,
with_phases,
)
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.state import next_epoch_via_block
from eth2spec.test.helpers.deposits import (
prepare_state_and_deposit,
@ -10,7 +11,7 @@ from eth2spec.test.helpers.deposits import (
from eth2spec.test.helpers.withdrawals import set_validator_fully_withdrawable
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_top_up_to_withdrawn_validator(spec, state):
validator_index = 0

View File

@ -1,8 +1,9 @@
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.execution_payload import (
build_empty_execution_payload,
)
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_capella_and_later
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_phases
from eth2spec.test.helpers.state import next_slot
@ -53,7 +54,7 @@ def run_withdrawals_processing(spec, state, execution_payload, valid=True):
assert state.withdrawal_queue == pre_withdrawal_queue[num_withdrawals:]
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_empty_queue(spec, state):
assert len(state.withdrawal_queue) == 0
@ -64,7 +65,7 @@ def test_success_empty_queue(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_in_queue(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -75,7 +76,7 @@ def test_success_one_in_queue(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_max_per_slot_in_queue(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
@ -86,7 +87,7 @@ def test_success_max_per_slot_in_queue(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_a_lot_in_queue(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
@ -101,7 +102,7 @@ def test_success_a_lot_in_queue(spec, state):
# Failure cases in which the number of withdrawals in the execution_payload is incorrect
#
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_empty_queue_non_empty_withdrawals(spec, state):
assert len(state.withdrawal_queue) == 0
@ -118,7 +119,7 @@ def test_fail_empty_queue_non_empty_withdrawals(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_one_in_queue_none_in_withdrawals(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -130,7 +131,7 @@ def test_fail_one_in_queue_none_in_withdrawals(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_one_in_queue_two_in_withdrawals(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -142,7 +143,7 @@ def test_fail_one_in_queue_two_in_withdrawals(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD)
@ -154,7 +155,7 @@ def test_fail_max_per_slot_in_queue_one_less_in_withdrawals(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
@ -170,7 +171,7 @@ def test_fail_a_lot_in_queue_too_few_in_withdrawals(spec, state):
# Failure cases in which the withdrawals in the execution_payload are incorrect
#
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_incorrect_dequeue_index(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -182,7 +183,7 @@ def test_fail_incorrect_dequeue_index(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_incorrect_dequeue_address(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -194,7 +195,7 @@ def test_fail_incorrect_dequeue_address(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_incorrect_dequeue_amount(spec, state):
prepare_withdrawal_queue(spec, state, 1)
@ -206,7 +207,7 @@ def test_fail_incorrect_dequeue_amount(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_one_of_many_dequeued_incorrectly(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)
@ -224,7 +225,7 @@ def test_fail_one_of_many_dequeued_incorrectly(spec, state):
yield from run_withdrawals_processing(spec, state, execution_payload, valid=False)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_fail_many_dequeued_incorrectly(spec, state):
prepare_withdrawal_queue(spec, state, spec.MAX_WITHDRAWALS_PER_PAYLOAD * 4)

View File

@ -1,15 +1,16 @@
from random import Random
from eth2spec.test.context import (
with_capella_and_later,
with_phases,
spec_state_test,
)
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_to,
)
from eth2spec.test.helpers.random import (
randomize_state,
)
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_to,
)
from eth2spec.test.helpers.withdrawals import (
set_validator_fully_withdrawable,
)
@ -41,7 +42,7 @@ def run_process_full_withdrawals(spec, state, num_expected_withdrawals=None):
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_no_withdrawable_validators(spec, state):
pre_validators = state.validators.copy()
@ -50,7 +51,7 @@ def test_no_withdrawable_validators(spec, state):
assert pre_validators == state.validators
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_withdrawable_epoch_but_0_balance(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -62,7 +63,7 @@ def test_withdrawable_epoch_but_0_balance(spec, state):
yield from run_process_full_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -74,7 +75,7 @@ def test_withdrawable_epoch_but_0_effective_balance_0_balance(spec, state):
yield from run_process_full_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -86,7 +87,7 @@ def test_withdrawable_epoch_but_0_effective_balance_nonzero_balance(spec, state)
yield from run_process_full_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_no_withdrawals_but_some_next_epoch(spec, state):
current_epoch = spec.get_current_epoch(state)
@ -98,7 +99,7 @@ def test_no_withdrawals_but_some_next_epoch(spec, state):
yield from run_process_full_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_single_withdrawal(spec, state):
# Make one validator withdrawable
@ -110,7 +111,7 @@ def test_single_withdrawal(spec, state):
assert state.next_withdrawal_index == 1
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_multi_withdrawal(spec, state):
# Make a few validators withdrawable
@ -120,7 +121,7 @@ def test_multi_withdrawal(spec, state):
yield from run_process_full_withdrawals(spec, state, 3)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_all_withdrawal(spec, state):
# Make all validators withdrawable
@ -150,25 +151,25 @@ def run_random_full_withdrawals_test(spec, state, rng):
yield from run_process_full_withdrawals(spec, state, None)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_withdrawals_0(spec, state):
yield from run_random_full_withdrawals_test(spec, state, Random(444))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_withdrawals_1(spec, state):
yield from run_random_full_withdrawals_test(spec, state, Random(420))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_withdrawals_2(spec, state):
yield from run_random_full_withdrawals_test(spec, state, Random(200))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_withdrawals_3(spec, state):
yield from run_random_full_withdrawals_test(spec, state, Random(2000000))

View File

@ -1,10 +1,11 @@
import random
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_capella_and_later,
with_phases,
spec_state_test,
with_presets,
)
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to
from eth2spec.test.helpers.state import next_epoch
from eth2spec.test.helpers.random import randomize_state
@ -48,7 +49,7 @@ def run_process_partial_withdrawals(spec, state, num_expected_withdrawals=None):
assert state.next_withdrawal_index == pre_next_withdrawal_index + num_expected_withdrawals
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_no_withdrawable(spec, state):
pre_validators = state.validators.copy()
@ -57,7 +58,7 @@ def test_success_no_withdrawable(spec, state):
assert pre_validators == state.validators
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_no_max_effective_balance(spec, state):
validator_index = len(state.validators) // 2
@ -71,7 +72,7 @@ def test_success_no_max_effective_balance(spec, state):
yield from run_process_partial_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_no_excess_balance(spec, state):
validator_index = len(state.validators) // 2
@ -85,7 +86,7 @@ def test_success_no_excess_balance(spec, state):
yield from run_process_partial_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_excess_balance_but_no_max_effective_balance(spec, state):
validator_index = len(state.validators) // 2
@ -100,7 +101,7 @@ def test_success_excess_balance_but_no_max_effective_balance(spec, state):
yield from run_process_partial_withdrawals(spec, state, 0)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable(spec, state):
validator_index = len(state.validators) // 2
@ -109,7 +110,7 @@ def test_success_one_partial_withdrawable(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable_not_yet_active(spec, state):
validator_index = len(state.validators) // 2
@ -121,7 +122,7 @@ def test_success_one_partial_withdrawable_not_yet_active(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
validator_index = len(state.validators) // 2
@ -134,7 +135,7 @@ def test_success_one_partial_withdrawable_in_exit_queue(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable_exited(spec, state):
validator_index = len(state.validators) // 2
@ -146,7 +147,7 @@ def test_success_one_partial_withdrawable_exited(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
validator_index = len(state.validators) // 2
@ -158,7 +159,7 @@ def test_success_one_partial_withdrawable_active_and_slashed(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
validator_index = len(state.validators) // 2
@ -171,7 +172,7 @@ def test_success_one_partial_withdrawable_exited_and_slashed(spec, state):
yield from run_process_partial_withdrawals(spec, state, 1)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_two_partial_withdrawable(spec, state):
set_validator_partially_withdrawable(spec, state, 0)
@ -180,7 +181,7 @@ def test_success_two_partial_withdrawable(spec, state):
yield from run_process_partial_withdrawals(spec, state, 2)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_success_max_partial_withdrawable(spec, state):
# Sanity check that this test works for this state
@ -192,7 +193,7 @@ def test_success_max_partial_withdrawable(spec, state):
yield from run_process_partial_withdrawals(spec, state, spec.MAX_PARTIAL_WITHDRAWALS_PER_EPOCH)
@with_capella_and_later
@with_phases([CAPELLA])
@with_presets([MINIMAL], reason="not enough validators with mainnet config")
@spec_state_test
def test_success_max_plus_one_withdrawable(spec, state):
@ -226,37 +227,37 @@ def run_random_partial_withdrawals_test(spec, state, rng):
yield from run_process_partial_withdrawals(spec, state)
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_0(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(0))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_1(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(1))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_2(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(2))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_3(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(3))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_4(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(4))
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_random_5(spec, state):
yield from run_random_partial_withdrawals_test(spec, state, random.Random(5))

View File

@ -1,7 +1,7 @@
from eth2spec.test.context import (
with_capella_and_later, spec_state_test
with_phases, spec_state_test
)
from eth2spec.test.helpers.constants import CAPELLA
from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
)
@ -16,7 +16,7 @@ from eth2spec.test.helpers.withdrawals import (
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_successful_bls_change(spec, state):
index = 0
@ -39,7 +39,7 @@ def test_successful_bls_change(spec, state):
assert post_credentials[12:] == signed_address_change.message.to_execution_address
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_full_withdrawal_in_epoch_transition(spec, state):
index = 0
@ -57,7 +57,7 @@ def test_full_withdrawal_in_epoch_transition(spec, state):
assert state.balances[index] == 0
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_partial_withdrawal_in_epoch_transition(spec, state):
index = state.next_withdrawal_index
@ -81,7 +81,7 @@ def test_partial_withdrawal_in_epoch_transition(spec, state):
assert len(state.withdrawal_queue) == pre_withdrawal_queue_len
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_many_partial_withdrawals_in_epoch_transition(spec, state):
assert len(state.validators) > spec.MAX_WITHDRAWALS_PER_PAYLOAD
@ -106,7 +106,7 @@ def test_many_partial_withdrawals_in_epoch_transition(spec, state):
assert len(state.withdrawal_queue) == pre_withdrawal_queue_len + 1
@with_capella_and_later
@with_phases([CAPELLA])
@spec_state_test
def test_exit_and_bls_change(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit

View File

@ -606,7 +606,7 @@ def is_post_eip4844(spec):
with_altair_and_later = with_all_phases_except([PHASE0])
with_bellatrix_and_later = with_all_phases_except([PHASE0, ALTAIR])
with_capella_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX, EIP4844])
with_capella_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX])
with_eip4844_and_later = with_all_phases_except([PHASE0, ALTAIR, BELLATRIX, CAPELLA])

View File

@ -34,6 +34,7 @@ ALL_FORK_UPGRADES = {
PHASE0: ALTAIR,
ALTAIR: BELLATRIX,
BELLATRIX: CAPELLA,
CAPELLA: EIP4844,
}
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key not in FORKS_BEFORE_ALTAIR}