Skip to content
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

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft

feat: L3 support #437

wants to merge 14 commits into from

Conversation

ocdbytes
Copy link
Member

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:

  • Feature
  • Testing

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

@apoorvsadana apoorvsadana marked this pull request as draft December 23, 2024 03:40
@@ -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
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pending

Comment on lines 46 to 50
starknet-accounts = "0.11.0"
starknet-contract = "0.11.0"
starknet-core = { workspace = true }
starknet-providers = { workspace = true }
starknet-signers = { workspace = true }
Copy link
Contributor

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?

Copy link
Member Author

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 ?

Copy link
Contributor

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

crates/client/settlement_client/src/client.rs Outdated Show resolved Hide resolved
crates/client/settlement_client/src/client.rs Outdated Show resolved Hide resolved
crates/client/settlement_client/src/eth/mod.rs Outdated Show resolved Hide resolved
crates/client/settlement_client/src/starknet/utils.rs Outdated Show resolved Hide resolved
crates/client/settlement_client/src/starknet/mod.rs Outdated Show resolved Hide resolved
crates/client/settlement_client/src/starknet/mod.rs Outdated Show resolved Hide resolved

Ok(())
}
}
Copy link
Contributor

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

Copy link
Contributor

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?

Copy link
Member Author

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 ?

Copy link
Contributor

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)]
Copy link
Contributor

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?

Copy link
Member Author

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");
Copy link
Contributor

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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the flow right now
Screenshot 2024-12-28 at 6 16 49 PM

I think this is how the flow should be like
Screenshot 2024-12-28 at 6 27 02 PM

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

Comment on lines 213 to 214
BlockId::Number(starting_block),
BlockId::Number(latest_block_number),
Copy link
Contributor

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;
Copy link
Contributor

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
Copy link
Contributor

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

@Trantorian1 Trantorian1 added the research Research and exploration required before implementation label Dec 31, 2024
Copy link
Contributor

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?

Copy link
Member Author

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(())
}
}
Copy link
Contributor

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 {
Copy link
Contributor

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed sorry missed it.

Comment on lines +73 to +77
// ============================================================
// Stream Implementations :
// ============================================================
type StreamType: Stream<Item = Option<anyhow::Result<CommonMessagingEventData>>>;
async fn get_event_stream(&self, last_synced_event_block: LastSyncedEventBlock)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment can be better

Copy link
Member Author

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?
Copy link
Contributor

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

Copy link
Member Author

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)?;
Copy link
Contributor

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?

Copy link
Member Author

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 {
Copy link
Contributor

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)

Copy link
Contributor

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()),

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

Comment on lines +27 to +36
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>,
Copy link
Contributor

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?

Copy link
Member Author

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.

Comment on lines +54 to +74
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;
}
}
Copy link
Contributor

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?

Copy link
Member Author

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 {
Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding tests (In progress ⌛.....)

Copy link
Contributor

@Mohiiit Mohiiit left a 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.

crates/client/settlement_client/Cargo.toml Show resolved Hide resolved
crates/client/settlement_client/src/client.rs Show resolved Hide resolved
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> {
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

@Mohiiit Mohiiit Jan 10, 2025

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(
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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())
Copy link
Contributor

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/cli/chain_config_overrides.rs Show resolved Hide resolved
crates/node/src/main.rs Show resolved Hide resolved
Copy link
Contributor

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?

Copy link
Member Author

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 ?

Copy link
Contributor

@Mohiiit Mohiiit Jan 10, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
research Research and exploration required before implementation
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

4 participants