Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion sequencer/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6743,14 +6743,21 @@ mod test {
);

UpdateStateData::<
crate::SeqTypes,
SeqTypes,
RewardMerkleTreeV2,
{ RewardMerkleTreeV2::ARITY },
>::insert_merkle_nodes(&mut tx, proof, traversal_path, height)
.await?;
}
}

UpdateStateData::<
SeqTypes,
RewardMerkleTreeV2,
{ RewardMerkleTreeV2::ARITY },
>::set_last_state_height(&mut tx, 15)
.await?;

tx.commit().await?;

let result_height_5 = db.get_all_reward_accounts(5, 0, 100).await?;
Expand Down Expand Up @@ -6808,6 +6815,88 @@ mod test {
Ok(())
}

/// ensure get_all_reward_accounts fails when merklized state height
/// is behind the requested height
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn test_get_all_reward_accounts_check_state_height() -> anyhow::Result<()> {
let storage = SqlDataSource::create_storage().await;
let sql_options = tmp_options(&storage);
let db = SqlStorage::connect(Config::try_from(&sql_options)?).await?;

let validated_state = ValidatedState::default();
let instance_state =
NodeState::mock().with_genesis_version(DrbAndHeaderUpgradeVersion::version());
let genesis_leaf = LeafQueryData::<SeqTypes>::genesis::<
SequencerVersions<DrbAndHeaderUpgradeVersion, DrbAndHeaderUpgradeVersion>,
>(&validated_state, &instance_state)
.await;

let mut reward_tree = RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT);
let account1 = RewardAccountV2::from_str("0x0000000000000000000000000000000000000001")?;

let mut tx = db.write().await?;

let header_json = serde_json::to_value(genesis_leaf.header())?;

for height in [5i64, 10, 15, 20] {
query(
"INSERT INTO header (height, hash, payload_hash, timestamp, data)
VALUES ($1, $2, $3, $4, $5)",
)
.bind(height)
.bind(format!("hash_{height}"))
.bind("payload_hash")
.bind(0i64)
.bind(&header_json)
.execute(tx.as_mut())
.await?;
}

// Insert merkle data at height 5
reward_tree.update(account1, RewardAmount::from(1000u64))?;
let (_, proof) = reward_tree.lookup(account1).expect_ok().unwrap();
let traversal_path =
<RewardAccountV2 as ToTraversalPath<{ RewardMerkleTreeV2::ARITY }>>::to_traversal_path(
&account1,
reward_tree.height(),
);
UpdateStateData::<
SeqTypes,
RewardMerkleTreeV2,
{ RewardMerkleTreeV2::ARITY },
>::insert_merkle_nodes(&mut tx, proof, traversal_path, 5)
.await?;

// Set the merklized state height to 10
// less than max block height of 20
UpdateStateData::<
SeqTypes,
RewardMerkleTreeV2,
{ RewardMerkleTreeV2::ARITY },
>::set_last_state_height(&mut tx, 10)
.await?;

tx.commit().await?;

// Query at height 5 should succeed
let result = db.get_all_reward_accounts(5, 0, 100).await;
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), 1);

// Query at height 10 should succeed
let result = db.get_all_reward_accounts(10, 0, 100).await;
assert!(result.is_ok());

// Query at height 15 should fail
// state not yet processed
let result = db.get_all_reward_accounts(15, 0, 100).await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("not yet available"));

Ok(())
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn test_namespace_query_compat_v0_2() {
test_namespace_query_compat_helper(SequencerVersions::<FeeVersion, FeeVersion>::new())
Expand Down
13 changes: 12 additions & 1 deletion sequencer/src/api/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use hotshot_query_service::{
sql::{Config, SqlDataSource, Transaction},
storage::{
sql::{query_as, Db, TransactionMode, Write},
AvailabilityStorage, MerklizedStateStorage, NodeStorage, SqlStorage,
AvailabilityStorage, MerklizedStateHeightStorage, MerklizedStateStorage, NodeStorage,
SqlStorage,
},
VersionedDataSource,
},
Expand Down Expand Up @@ -274,6 +275,16 @@ impl CatchupStorage for SqlStorage {
"requested height {height} is not yet available (latest block height: {block_height})"
);

let merklized_state_height = tx
.get_last_state_height()
.await
.context("getting merklized state height")? as u64;
ensure!(
height <= merklized_state_height,
"requested height {height} is not yet available. latest merklized state height: \
{merklized_state_height}"
);

let header = tx
.get_header(BlockId::<SeqTypes>::from(height as usize))
.await
Expand Down
Loading