Skip to content

Commit 0be3ee6

Browse files
authored
hotfix(provider): pre-fork point contract state not being fetched correctly (#376)
1 parent 6d6073f commit 0be3ee6

File tree

3 files changed

+419
-17
lines changed

3 files changed

+419
-17
lines changed

crates/storage/provider/provider/src/providers/db/mod.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -763,24 +763,28 @@ impl<Tx: DbTxMut> BlockWriter for DbProvider<Tx> {
763763
}
764764

765765
for (addr, new_class_hash) in states.state_updates.replaced_classes {
766-
let mut info = self
767-
.0
768-
.get::<tables::ContractInfo>(addr)?
769-
.ok_or(ProviderError::MissingContractInfo { address: addr })?;
770-
771-
info.class_hash = new_class_hash;
772-
self.0.put::<tables::ContractInfo>(addr, info)?;
766+
let info = if let Some(info) = self.0.get::<tables::ContractInfo>(addr)? {
767+
GenericContractInfo { class_hash: new_class_hash, ..info }
768+
} else {
769+
GenericContractInfo { class_hash: new_class_hash, ..Default::default() }
770+
};
773771

774-
let mut change_set = self
775-
.0
776-
.get::<tables::ContractInfoChangeSet>(addr)?
777-
.ok_or(ProviderError::MissingContractInfoChangeSet { address: addr })?;
772+
let new_change_set =
773+
if let Some(mut change_set) = self.0.get::<tables::ContractInfoChangeSet>(addr)? {
774+
change_set.class_change_list.insert(block_number);
775+
change_set
776+
} else {
777+
ContractInfoChangeList {
778+
class_change_list: BlockList::from([block_number]),
779+
..Default::default()
780+
}
781+
};
778782

779-
change_set.class_change_list.insert(block_number);
780-
self.0.put::<tables::ContractInfoChangeSet>(addr, change_set)?;
783+
self.0.put::<tables::ContractInfo>(addr, info)?;
781784

782785
let class_change_key = ContractClassChange::replaced(addr, new_class_hash);
783786
self.0.put::<tables::ClassChangeHistory>(block_number, class_change_key)?;
787+
self.0.put::<tables::ContractInfoChangeSet>(addr, new_change_set)?;
784788
}
785789

786790
for (addr, nonce) in states.state_updates.nonce_updates {

crates/storage/provider/provider/src/providers/fork/state.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,30 @@ impl<Tx1: DbTx> ContractClassProvider for LatestStateProvider<Tx1> {
120120

121121
impl<Tx1: DbTx> StateProvider for LatestStateProvider<Tx1> {
122122
fn nonce(&self, address: ContractAddress) -> ProviderResult<Option<Nonce>> {
123-
if let res @ Some(..) = self.local_provider.nonce(address)? {
124-
Ok(res)
123+
if let Some(nonce) = self.local_provider.nonce(address)? {
124+
// TEMPFIX:
125+
//
126+
// This check is required due to the limitation on how we're storing updates for
127+
// contracts that were deployed before the fork point. For those contracts,
128+
// their corresponding entries for the `ContractInfo` table might not exist.
129+
// In which case, `BlockWriter::insert_block_with_states_and_receipts`
130+
// implementation of `ForkedProvider` would simply defaulted the fields to zero
131+
// (depending which field is being updated). Thus, this check is to
132+
// determine if the this contract's info is split across the local and fork dbs, where
133+
// the value zero means the data is stored in the forked db.
134+
//
135+
// False positive:
136+
//
137+
// Nonce can be zero
138+
if nonce == Nonce::ZERO {
139+
if let Some(nonce) = self.fork_provider.db.provider().latest()?.nonce(address)? {
140+
if nonce != Nonce::ZERO {
141+
return Ok(Some(nonce));
142+
}
143+
}
144+
}
145+
146+
Ok(Some(nonce))
125147
} else if let Some(nonce) =
126148
self.fork_provider.backend.get_nonce(address, self.fork_provider.block_id)?
127149
{
@@ -147,8 +169,32 @@ impl<Tx1: DbTx> StateProvider for LatestStateProvider<Tx1> {
147169
&self,
148170
address: ContractAddress,
149171
) -> ProviderResult<Option<ClassHash>> {
150-
if let res @ Some(..) = self.local_provider.class_hash_of_contract(address)? {
151-
Ok(res)
172+
if let Some(hash) = self.local_provider.class_hash_of_contract(address)? {
173+
// TEMPFIX:
174+
//
175+
// This check is required due to the limitation on how we're storing updates for
176+
// contracts that were deployed before the fork point. For those contracts,
177+
// their corresponding entries for the `ContractInfo` table might not exist.
178+
// In which case, `BlockWriter::insert_block_with_states_and_receipts`
179+
// implementation of `ForkedProvider` would simply defaulted the fields to zero
180+
// (depending which field is being updated). Thus, this check is to
181+
// determine if the this contract's info is split across the local and fork dbs, where
182+
// the value zero means the data is stored in the forked db.
183+
//
184+
// False positive:
185+
//
186+
// Some contracts can have class hash of zero (ie special contracts like 0x1, 0x2) hence
187+
// why we simply return the value if it can't be found in the forked db.
188+
// This is very hacky but it works for now.
189+
if hash == ClassHash::ZERO {
190+
if let Some(hash) =
191+
self.fork_provider.db.provider().latest()?.class_hash_of_contract(address)?
192+
{
193+
return Ok(Some(hash));
194+
}
195+
}
196+
197+
Ok(Some(hash))
152198
} else if let Some(class_hash) =
153199
self.fork_provider.backend.get_class_hash_at(address, self.fork_provider.block_id)?
154200
{

0 commit comments

Comments
 (0)