-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: L3 support #437
base: main
Are you sure you want to change the base?
feat: L3 support #437
Conversation
while !page_indicator { | ||
let events = provider | ||
.get_events( | ||
EventFilter { | ||
from_block: filter.from_block, | ||
to_block: filter.to_block, | ||
address: filter.address, | ||
keys: filter.keys.clone(), | ||
}, | ||
continuation_token.clone(), | ||
1000, | ||
) | ||
.await?; | ||
|
||
event_vec.extend(events.events); | ||
if let Some(token) = events.continuation_token { | ||
continuation_token = Some(token); | ||
} else { | ||
page_indicator = true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code exists in get_events
in mod.rs as well, can we use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That cannot be used unfortunately as there are some constraints in stream implementation.
So when I used the function it kills the stream when no event is returned. So there are no new calls to the stream.
I am checking more on this behaviour on why this is happening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need some more context in order to understand the event structure a bit better, by structure I mean, how we are fetching for both cases and how is cancelling of messages is working.
let block_number = self.provider.get_block_number().await?.as_u64(); | ||
Ok(block_number) | ||
} | ||
|
||
/// Get the block number of the last occurrence of a given event. | ||
pub async fn get_last_event_block_number<T: SolEvent>(&self) -> anyhow::Result<u64> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why have we removed this? ideally this function should be able to filter out on any event right? if not let's update it's name and comment accordingly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not removed get_last_event_block_number
function still exists although it is not used anywhere, but it is still there in the client trait implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my bad for wrong comment, I meant why we removed <T: SolEvent>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So basically It was removed to make the ClientTrait more generalised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh I see, so can we change the name of the function here? get_last_state_update_event_block_number
, given it was generic earlier?
|
||
// Initialize database service | ||
let db = Arc::new( | ||
DatabaseService::new(&base_path, backup_dir, false, chain_info.clone(), Default::default()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open_for_testing
can be used instead of new
crates/node/src/service/l1.rs
Outdated
devnet: bool, | ||
mempool: Arc<Mempool>, | ||
) -> anyhow::Result<Box<dyn Service>> { | ||
match config.settlement_layer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we add the if !config.l1_sync_disabled && (config.l1_endpoint.is_some() || !devnet) else {}
part that is common in eth and starknet here? we can have this match statement inside the if part and it will return the client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as discussed this common logic should be in the create function
// Get the latest block number | ||
async fn get_latest_block_number(&self) -> anyhow::Result<u64>; | ||
|
||
// Get the block number of the last occurrence of a specific event |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's change the comment, given it's not generic now
// Get the block number of the last occurrence of a specific event | |
// Get the block number of the last occurrence of the state update event |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ok(event_data) => { | ||
assert_eq!(event_data.block_number, 100); | ||
assert_eq!(event_data.event_index, Some(0u64)); | ||
// Add more assertions as needed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this WIP?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nop removed missed it used claude for generating the tests.
async fn process_message( | ||
backend: &MadaraBackend, | ||
event: &CommonMessagingEventData, | ||
_chain_id: &ChainId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove this? given it's not required
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this param was there in the eth implementation also so I thought maybe it is left for some future functionality. Should I remove this ?
// Block number management in case of pending block number events. | ||
match event.block_number { | ||
Some(block_number) => Ok(block_number), | ||
None => Ok(self.get_latest_block_number().await? + 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in starknet, we get events as soon as the transactions are executed? and that's why we might get last event from the pending block?
@@ -1,37 +1,32 @@ | |||
use std::sync::Arc; | |||
|
|||
<<<<<<<< HEAD:crates/madara/client/settlement_client/src/state_update.rs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have this in primitives/utils/src/hash.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed maybe left during the merge
crates/madara/client/settlement_client/src/starknet/test_contracts/appchain_test.cairo
Outdated
Show resolved
Hide resolved
while let Some(events) = ctx | ||
.run_until_cancelled(self.get_events( | ||
BlockId::Number(self.get_latest_block_number().await?), | ||
BlockId::Number(self.get_latest_block_number().await?), | ||
self.l2_core_contract, | ||
vec![get_selector_from_name("LogStateUpdate")?], | ||
)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can move this to a closure for better readability as discussed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally we should create an issue for a test case/inform MSL where if a block has 2 events and madara processes one and shuts down, then on restart the next message is picked up of that same block
} | ||
Poll::Pending => Poll::Pending, | ||
}, | ||
None => Poll::Ready(Some(None)), // Handle the case where future is None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think we should throw an error here to inform users something went wrong
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I glossed over the last few files, so there could be some stuff I didn't catch. Overall this seems interesting but needs as few changes here and there, nothing major.
# TODO : For now madara binary is stored in aws s3 bucket : | ||
# After the proper release binaries are implemented | ||
# We can directly use that and we can remove this | ||
# temporary AWS implementation | ||
- name: Download madara binary for l2 client testing | ||
run: | | ||
curl -L https://madara-test-binary.s3.us-west-1.amazonaws.com/madara-linux -o ./test-artifacts/madara | ||
chmod +x ./test-artifacts/madara | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we downloading a madara binary for testing instead of running against a build of the code in the pr?
- name: Download madara binary for l2 client testing | ||
run: | | ||
curl -L https://madara-test-binary.s3.us-west-1.amazonaws.com/madara-linux -o ./test-artifacts/madara | ||
chmod +x ./test-artifacts/madara |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
- name: Launch Anvil | ||
run: anvil --fork-url $ANVIL_FORK_URL --fork-block-number $ANVIL_BLOCK_NUMBER & | ||
env: | ||
ANVIL_FORK_URL: "https://eth.merkle.io" | ||
ANVIL_BLOCK_NUMBER: 20395662 | ||
- name: Wait for Anvil to be ready | ||
run: | | ||
while ! nc -z localhost 8545; do | ||
sleep 1 | ||
done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure this is necessary? If I remember the rust unit tests should start anvil anyway no?
[[package]] | ||
name = "serial_test" | ||
version = "3.2.0" | ||
source = "registry+https://github.com/rust-lang/crates.io-index" | ||
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" | ||
dependencies = [ | ||
"futures", | ||
"log", | ||
"once_cell", | ||
"parking_lot 0.12.3", | ||
"scc", | ||
"serial_test_derive", | ||
] | ||
|
||
[[package]] | ||
name = "serial_test_derive" | ||
version = "3.2.0" | ||
source = "registry+https://github.com/rust-lang/crates.io-index" | ||
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" | ||
dependencies = [ | ||
"proc-macro2", | ||
"quote", | ||
"syn 2.0.89", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be here. I believe we removed serial-test from madara so the CI can run in parallel. Consider implementing a locking mechanism by hand otherwise, I am not convinced we need an external crate for this kind of simple functionality.
@@ -222,6 +220,7 @@ dotenv = "0.15.0" | |||
httpmock = "0.7.0" | |||
tempfile = "3.10.1" | |||
mockall = "0.13.0" | |||
serial_test = "3.1.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
} | ||
|
||
#[rstest] | ||
#[traced_test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
#[tokio::test] | ||
// This test is redundant now as the event poller will not return the same | ||
// event twice with same nonce that's why added ignore here. | ||
#[ignore] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is redundant then consider removing it
} | ||
|
||
#[rstest] | ||
#[traced_test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
use tempfile::TempDir; | ||
use url::Url; | ||
|
||
#[rstest] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using rstest
if there is no fixture
?
backend: Arc<MadaraBackend>, | ||
settlement_client: Arc<Box<dyn ClientTrait<Config = C, StreamType = S>>>, | ||
chain_id: ChainId, | ||
l1_gas_provider: GasPriceProvider, | ||
gas_price_sync_disabled: bool, | ||
gas_price_poll_ms: Duration, | ||
mempool: Arc<Mempool>, | ||
ctx: ServiceContext, | ||
l1_block_metrics: Arc<L1BlockMetrics>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider refactoring some of these arguments behind a struct.
@@ -3,6 +3,7 @@ | |||
## Next release | |||
|
|||
- fix(primitives): limit legacy class sizes | |||
- feat : l3 support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can remove it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update the dependencies to use workspace instead of fixed versions to ensure consistency, avoid version conflicts, and simplify dependency management
#[derive(Debug, Default, PartialEq)] | ||
pub struct DummyConfig; | ||
pub type DummyStream = BoxStream<'static, Option<anyhow::Result<CommonMessagingEventData>>>; | ||
|
||
#[automock( | ||
type Config = DummyConfig; | ||
type StreamType = DummyStream; | ||
)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving this behind a "test" feature flag to avoid including them in production builds.
/// - 0 if the message has not been cancelled | ||
/// - timestamp of the cancellation if it has been cancelled | ||
/// - An Error if the call fail | ||
async fn get_l1_to_l2_message_cancellations(&self, msg_hash: Vec<u8>) -> anyhow::Result<Felt>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
async fn get_l1_to_l2_message_cancellations(&self, msg_hash: Vec<u8>) -> anyhow::Result<Felt>; | |
async fn get_l1_to_l2_message_cancellations(&self, msg_hash: &[u8]) -> anyhow::Result<Felt>; |
// Create a new instance of the client | ||
async fn new(config: Self::Config) -> anyhow::Result<Self> | ||
where | ||
Self: Sized; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider removing new
from the trait and implementing it as an associated function in the struct to avoid issues with Self: Sized
, async trait limitations, and the inability to use it with dyn ClientTrait
let current_processed = self.processed_update_state_block.load(Ordering::Relaxed); | ||
if current_processed < block_number { | ||
let formatted_event = | ||
StateUpdate { block_number, global_root: data.data[0], block_hash: data.data[2] }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto (direct access)
let events_fetched = events?; | ||
if let Some(event) = events_fetched.last() { | ||
let data = event; | ||
let block_number = data.data[1].to_u64().ok_or(anyhow!("Block number conversion failed"))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto (direct access)
fn event_to_felt_array(&self, event: &CommonMessagingEventData) -> Vec<Felt> { | ||
let mut felt_vec = vec![event.from, event.to, event.selector, event.nonce]; | ||
felt_vec.push(Felt::from(event.payload.len())); | ||
event.payload.clone().into_iter().for_each(|felt| { | ||
felt_vec.push(felt); | ||
}); | ||
|
||
felt_vec | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn event_to_felt_array(&self, event: &CommonMessagingEventData) -> Vec<Felt> { | |
let mut felt_vec = vec![event.from, event.to, event.selector, event.nonce]; | |
felt_vec.push(Felt::from(event.payload.len())); | |
event.payload.clone().into_iter().for_each(|felt| { | |
felt_vec.push(felt); | |
}); | |
felt_vec | |
} | |
fn event_to_felt_array(&self, event: &CommonMessagingEventData) -> Vec<Felt> { | |
std::iter::once(event.from) | |
.chain(std::iter::once(event.to)) | |
.chain(std::iter::once(event.selector)) | |
.chain(std::iter::once(event.nonce)) | |
.chain(std::iter::once(Felt::from(event.payload.len()))) | |
.chain(event.payload.iter().cloned()) | |
.collect() | |
} |
} | ||
} | ||
|
||
pub fn starknet_account() -> anyhow::Result<StarknetAccount> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it only for testing?
db_backend: Arc<MadaraBackend>, | ||
eth_client: Option<Arc<EthereumClient>>, | ||
settlement_client: Option<Arc<Box<dyn ClientTrait<Config = C, StreamType = S>>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is Box
really necessary?
Added support for L3s. Starknet client is added in this PR and tests are added for several client functions. for now we are assuming that the l3 gas prices will be zero only.
Pull Request type
Please add the labels corresponding to the type of changes your PR introduces:
What is the current behavior?
Currently we didn't support the L3 appchain spec.
What is the new behavior?
Included the starknet client and updated the cli arguments for running madara in appchain mode. For now for all the gas prices and metrics we are still using the l1 modules. We can cover this refactoring in another PR.
Key Changes
crates/madara/client/eth
is renamed tocrates/madara/client/settlement_client
:.watch()
function for listening to events we have added a custom stream implementation for starknet (Starknet Stream Implementation)eth_core_contract_address
andeth_gps_statement_verifier
in chain config in order to be more accomodating to other types we might use in future and we are currently using.Important
For now as the PR is itself very long and connot be breaked into components because of inter component and e2e messaging tests. All the variables which are related to l1 are not renamed rn as it will be more time consuming and has no impact on functionality as of now.
Simple Diagram for messaging compoenents
Does this introduce a breaking change?
No