-
Notifications
You must be signed in to change notification settings - Fork 26
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
Validator needs to reconstruct its state from historical blockchain data #458
Comments
Validator Historical DataAt the moment the validator has started, there may already be existing active storage requests containing slots with slot ids that should be monitored by the validator: these are slot ids that either belong to the group observed by the validator or basically any slot id if validator is to monitor the whole slot id space. In this context we want the validator to find and monitor all those slots id associated with storage requests that started earlier and have not yet concluded. max duration of a storage requestCurrently it does not look like the duration of the storage request is limited. In this case we need to pull the history events from some pre-configured date (it does not make sense to pull the history from ancient times when Codex did not exist). getting historical dataTo get the history data we can use a function similar to the one already defined on method queryPastEvents*[T: MarketplaceEvent](
market: OnChainMarket,
_: type T,
blocksAgo: int): Future[seq[T]] {.async.} =
convertEthersError:
let contract = market.contract
let provider = contract.provider
let head = await provider.getBlockNumber()
let fromBlock = BlockTag.init(head - blocksAgo.abs.u256)
return await contract.queryFilter(T,
fromBlock,
BlockTag.latest)
We can add a new function taking a To compute the block number at the
Filtering eventsI would start with the following procedure (let's call it
I am doing some small spike (as I like to experience some of the libraries in isolation, here |
Nice design @marcinczenko 🙌
Apart from in the tests, the usage of this has been removed from the codebase as it was no longer needed. However, I knew we'd be using this function again so it was left in :) Usage is like so: let reqs = await market.queryPastEvents(StorageRequested, 5)
We will be launching mainnet on our own Linea L2, I believe? The mainnet block times seem to be around 2s for now: I like your idea of monitoring the block times, however my opinion is that for now, we find out what the average will be for the L2 we deploy on, and use that as a "hardcoded" average time. With that time, you can find the approximate block number ( For now, we are targeting 30 days as the maximum request duration, which is likely to be gradually increased in the future.
The validator does this already in |
Didn't you mean
Indeed that is pretty much certain. I still keep thinking of Ethereum for some reason.
30 days - seems good to me. Initially thought that your suggestion to perform flat search would be simpler than doing binary search, but after thinking a bit I am not that convinced anymore. The average block time on Linea (https://lineascan.build/chart/blocktime) seems to be quite stable at the moment (fluctuations are below 5% on daily average). Yet, how much can we depend on it I do not know. For
Yes, if the number of observed slots grows to a large number, we would indeed start to feel it. Batching may indeed help, maybe there is somewhere else. Not sure if we should immediately worry about? If that would be up to me only, I would probably start simple without any optimisation. Feels to me a serious thing, but maybe it can wait till get some first users?
In my opinion this a logical consequence of reconstructing the state. But I also agree (plus the same reasons as stated in the previous point), that it can wait a bit. I think this should happen before being tempted to do any batching or similar optimisation. With persistence in place, we should be able to respond to high demand even after periods of inactivity.
I think it should be enough. Just remember that this last stage of filtering of the log will happen not earlier than at the end of the current period (because of |
From what I understand, we will have our own side chain, so my guess is block times will be mostly stable and we'll know when it is expected to change. Of course, that is a guess :) The binary search sounds like a good idea. How will you know how far back (in blocks) to go on the first search?
I don't feel like batching would be a lot of effort, but yeah maybe it's best to get this working first without optimisations. However, I'd also like to hear what others' opinions are. My thoughts are that the request latency is the biggest issue. Even if validators run their own execution client, at 50ms total for a round trip (a single The batch could be a simple view in the contract that returns the state of 10 slots in one call. The validator would get all
Good point. Maybe it would be better to do it beforehand then. |
I am still doing initial estimate based on the average time between blocks and then I am adjusting I am doing something like this to initialise var low = 0
var high = latestBlockNumber
if estimatedBlockTimestamp < epochTimeUInt256:
low = estimatedBlockNumber
else:
high = estimatedBlockNumber
I feel the same. When saying that it is "a serious thing" I meant that we do need to have it in mind. So yes, I agree we need to have that on the horizon. I would probably first go for state preservation and then this, but I am open to a different order as well (at least lets keep both things as separate issues/PRs, so that things are easier to get merged).
I was tempted to let the validator to ask for all slots it wants in one batch, but that indeed could grow to big. So yes, this sounds like something to try (although the max number in one batch can probably be bigger). |
Here is a small chain independent demo that I used to play with search without depending on the blockchains before porting it to our code: https://play.nim-lang.org/#pasty=ECupqAtx |
Initially I planned to use the average block time to predict the number of blocks we need to look back in order to find the block number corresponding to the given epoch time. This estimation can be highly inaccurate if block time has changed in the past or is fluctuating and therefore we used that information initially only to find out if the available history is long enough to perform effective search. It turns out we do not have to do that. There is an easier way. First we check if the given epoch time equals the timestamp of either the earliest or the latest block. If it does, we just return the block number of that block. Otherwise, if the earliest available block is not the genesis block, we should check the timestamp of that earliest block and if it is greater than the epoch time, we should issue a warning and return that earliest block number. In all other cases, thus when the earliest block is not the genesis block but its timestamp is not greater than the requested epoch time, or if the earliest available block is the genesis block, Additional benefit of this method is that we do not have to rely on the average block time, which not only makes the whole thing more reliable, but also easier to test. Basically, the whole check has been simplified to the following: # Are lucky today?
if earliestBlockTimestamp == epochTime:
return earliestBlockNumber
if latestBlockTimestamp == epochTime:
return latestBlockNumber
if earliestBlockNumber > 0 and earliestBlockTimestamp > epochTime:
warn "Short block history detected.", earliestBlockTimestamp =
earliestBlockTimestamp
return earliestBlockNumber |
As part of the improvement in #457 we also need to add the ability to reconstruct the validator state (or make it persistent with some sort of database) when it boots up as the validator needs to validate also requests that were started before the validator booted up. Currently it only starts validating new requests.
The text was updated successfully, but these errors were encountered: