-
Notifications
You must be signed in to change notification settings - Fork 46
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
@@ -68,3 +68,6 @@ tmp/ | |||
# Running madara with make and docker compose | |||
.secrets | |||
image.tar.gz | |||
|
|||
# madara test artifacts for testing l2 clients for appchains | |||
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.
should this be in the root folder?
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.
According to me ig test-artifacts folder should be there because we also have a devnet.yaml file in 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.
but why in root? it should be where the testcase is right
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.
pending
starknet-accounts = "0.11.0" | ||
starknet-contract = "0.11.0" | ||
starknet-core = { workspace = true } | ||
starknet-providers = { workspace = true } | ||
starknet-signers = { workspace = 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.
should we put these behind feature flags?
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 these are workspace dependencies they are already being built. Do you think it will be reducing any build time ?
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.
workspace dependencies should not be built unless being used in a crate afaik. this is also a good time to check if we should move starknet/eth into crates btw
|
||
Ok(()) | ||
} | ||
} |
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 we test listen_for_update_state_events
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.
oh ok, I noticed we've a test for this in the common starknet/eth file. can we move it inside eth and starknet because they've different logics?
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.
But shouldn't the tests be with the module ?
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 listen_for_update_state_events
now be removed in favor of our event stream?
l1_gas_provider, | ||
chain_id, | ||
gas_price_sync_disabled: !gas_price_sync_enabled, | ||
gas_price_poll, | ||
mempool, | ||
}) | ||
} | ||
|
||
// Factory method to create the appropriate service | ||
#[allow(clippy::too_many_arguments)] |
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 make it a struct?
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 tried but lifetime specifier ka issue aa rha h. will try it in next commit.
) | ||
.await?; | ||
// Ensure correct read call : u256 (0, 0) | ||
assert_eq!(call_res.len(), 2, "l1_to_l2_message_cancellations should return only 2 values"); |
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.
same as before
} | ||
} | ||
|
||
async fn listen_for_messaging_events( |
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 this is how the flow should be like
You should look at this for implementing streams - https://docs.rs/futures/latest/futures/stream/trait.Stream.html
This is what I think alloy is also using fro event streams
BlockId::Number(starting_block), | ||
BlockId::Number(latest_block_number), |
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.
what happens if the gap is too big between start and end block? do RPCs throw an error? I think there's like a max 10k gap limit but we should confirm
} | ||
} | ||
|
||
sleep(Duration::from_secs(5)).await; |
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 can reduce this time to ms I think
} else if latest_block_number < 100 { | ||
0 | ||
} else { | ||
latest_block_number - 100 |
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.
instead of looping over last 100 blocks every time, I think we can keep state in memory here. the stream implementation i linked to allows for 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.
why do a folder for messaging?
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.
Initially made for the message processor approach will remove.
|
||
Ok(()) | ||
} | ||
} |
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 listen_for_update_state_events
now be removed in favor of our event stream?
// L2 (Starknet) <-> L3 messaging format | ||
// GitHub Ref : https://github.com/cartridge-gg/piltover/blob/saya/src/messaging/component.cairo#L85 | ||
#[derive(Clone)] | ||
pub struct MessageSent { |
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 don't think this is being used anywhere
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 sorry missed it.
// ============================================================ | ||
// Stream Implementations : | ||
// ============================================================ | ||
type StreamType: Stream<Item = Option<anyhow::Result<CommonMessagingEventData>>>; | ||
async fn get_event_stream(&self, last_synced_event_block: LastSyncedEventBlock) |
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.
comment can be better
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.
Adding a proper description.
// Stream implementation for settlement client.
// StreamType represents the data returned by the stream and the implemented version should always return `CommonMessagingEventData`
type StreamType: Stream<Item = Option<anyhow::Result<CommonMessagingEventData>>>;
// This function should return the event stream implementation that is adhering to the `StreamType` return type.
async fn get_event_stream(&self, last_synced_event_block: LastSyncedEventBlock) -> anyhow::Result<Self::StreamType>;
This description works ? Or should I do like more elaborate description about the stream implementation design ?
let res = mempool.accept_l1_handler_tx(transaction.into(), fees.unwrap_or(0))?; | ||
|
||
// TODO: remove unwraps | ||
// Ques: shall it panic if no block number of event_index? |
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 block_num and event_index optional
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.
event index is optional as initially also it was optional. Now have made the block_number as non option.
// TODO: remove unwraps | ||
// Ques: shall it panic if no block number of event_index? | ||
let block_sent = LastSyncedEventBlock::new(settlement_layer_block_number.unwrap(), event_index.unwrap_or(0)); | ||
backend.messaging_update_last_synced_l1_block_with_event(block_sent)?; |
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.
isn't this also happening in the caller?
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 from process_message
function.
Some(u128::from_be_bytes(padded.try_into().unwrap())) | ||
} | ||
|
||
fn to_hex_string(bytes: &[u8]) -> String { |
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.
hex::encode
should work here (already imported)
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.
hex::encode(event_data.transaction_hash.clone().unwrap_or(vec![0]).as_slice()),
hex::encode(event_data.from.as_slice()),
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.
updated.
pub from: Vec<u8>, | ||
pub to: Vec<u8>, | ||
pub selector: Vec<u8>, | ||
pub nonce: Vec<u8>, | ||
pub payload: Vec<Vec<u8>>, | ||
pub fee: Option<Vec<u8>>, | ||
pub transaction_hash: Option<Vec<u8>>, | ||
pub message_hash: Option<Vec<u8>>, | ||
pub block_number: Option<u64>, | ||
pub event_index: Option<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.
does everything need to be optional + raw u8 type? i think we should be able to reuse some types like ContractAddress
etc. right?
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 have changed the block_number as non optional.
Current implementation :
#[derive(Clone)]
pub struct CommonMessagingEventData {
pub from: Vec<u8>,
pub to: Vec<u8>,
pub selector: Vec<u8>,
pub nonce: Vec<u8>,
pub payload: Vec<Vec<u8>>,
pub fee: Option<Vec<u8>>, // not returned in L2 event
pub transaction_hash: Vec<u8>,
pub message_hash: Option<Vec<u8>>, // not returned in L1 event
pub block_number: u64,
pub event_index: Option<u64>, // not returned in L2 event
}
I chose Vec as it can be converted into any type for both ethereum and starknet and type varies across the different clients.
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.
|
||
type FutureType = Pin<Box<dyn Future<Output = anyhow::Result<(Option<EmittedEvent>, EventFilter)>> + Send>>; | ||
|
||
pub struct StarknetEventStream { |
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 also need tests for this and the eth stream right?
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.
Adding tests (In progress ⌛.....)
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>
?
// Stream Implementations : | ||
// ============================================================ | ||
type StreamType = EthereumEventStream; | ||
async fn get_event_stream( |
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 should change it's name or we can it more generic?
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 you give an example ? Ik the current stream implementation is not the best approach as there are many things I am also learning when I am writing tests for streams.
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.
by generic I mean, instead of using hardcoded event, what if we make it take which would be solEvent and then pass on that to the function below? in future we would be able to use this function to create event stream of any event? wdyt?
|
||
// 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
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 can fetch it from the configs/presets/devnet.yaml
?
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.
Makes sense but I changed the time in this devnet.yaml. Should I write a shell script to change that particular param ?
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 use --chain-config-override
cli param to update the block_time and other stuff too if you need
example: --chain-config-override=block_time=30s,pending_block_update_time=2s
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.
Does this introduce a breaking change?
No