solana-labs / solana

Web-Scale Blockchain for fast, secure, scalable, decentralized apps and marketplaces.
https://solanalabs.com
Apache License 2.0
12.91k stars 4.13k forks source link

Cron for Solana Runtime #17145

Closed jon-chuang closed 1 year ago

jon-chuang commented 3 years ago

Problem

Although it is easy for an off-chain agent to perform the role of cron in scheduling transactions, many would benefit from the ease of use of a scheduling functionality available on-chain.

Proposed Solution

Maintain a Hashmap of SlotCronJobList accounts and "sleep" till next job. SlotCronJobLists contain the prev_job_slot and next_job_slot fields, so that the hashmap can be updated in a doubly-linked list fashion. Essentially, we are implementing a queue maintaining job order, where execution for a job can only start after a timestamp, but potentially run any time after.

A job can either skip_if_missed_slot or reschedule, or skip_if_missed_n_slots(u64). i.e.

enum CronJobType {
  KeepTrying,
  SkipIfMissed1,
  SkipIfMissedN(u64),
  RescheduleBy(u64),
  RescheduleIfMissedNBy((u64, u64)),
}

2nd and 4th can be captured by 3rd and 5th, I guess.

CronJobs also have another orthogonal param:

enum CronJobRecurrence {
  // Every _ epochs
  AllTime(u64),
  // Recurrence limit and invoked every _ epochs
  FixedRecurrence((u64,u64)),
}

After which the jobs are deleted.

Every cron job item processing invocation is paid for by a corresponding fee-paying account.

If an account has insufficient balance, it deletes the jobs.


So all runtimes do the following:

There is a central cron account. Cron program updates the next_job_slot. Every slot, each validator checks the if the slot is the next_job_slot. If so, it wakes up. This way, we can avoid unnecessary BTree queries.

Everytime a new job is inserted, if its job_slot is < next_job_slot, next_job_slot is updated, etc

If a block is skipped, the cron runtime will detect a discrepancy in that next_job_slot is < current_slot and attempts to execute jobs from the skipped next job slot till present, skipping those that are marked as skip_if_missed_slot . We can get to reschedule in the future. The default is to execute the jobs belatedly, in order.

With regards to amount of crontab work not exceeding capacity, for a given slot, this would have to be first come first serve.

But anw since crontab processes transactions, it's equivalent to say it's hard to guarantee the amount of txs won't exceed capacity of the network.

Nonetheless, I can definitely preempt a potential DDoS attack which is to spam TXs to schedule cronjobs for a particular slot. But this is fixable by implementing a const MAX_CRON_JOBS_PER_SLOT: u64 = 1 << 14. And we return CronJobError::CapacityForSlotExceeded if we hit that limit.

Users need to know that there is no guarantee a cronjob would execute in a particular epoch, but then again, that is true also for a tx


Access control: So the protocol I've just thought of works as follows: 1.To register your account with the cron program, first, you have to: atomically transfer ownership to the cron program + register your ownership in the cron registry.

  1. When you want to schedule a tx, you can first sign it with information about the time you want it to be run. You pay a fee for the service. The cron program checks your pubkey corresponds to its registry of ownership for the account it has to modify. If these checks are valid, the tx is accepted and it schedules your tx.
  2. When the time comes, the cron program, which is the authority, can sign a tx pertaining to that account.

What is Cron after all but a pupeteer?

I'm starting to conceptualise it as having a puppeteer program that acts on your behalf on-chain. It has its own registry of access control, similar to other programs with derived addresses.

Meaning - doesn't an SPL-token account need to have the corresponding registry/from pubkey account-derivation method?

One of the most important thing this means is you can schedule txs to be signed at arbitrarily chosen times.

In a distant future this could also mean you can listen to on-chain events and react autonomously, even if that means signing (authorising) txs.

You need to use cron to do everything on your behalf - create SPL-token accounts etc.

Cron would need to be integrated into wallets and need to be lightweight so that it incurs a minimal overhead to perform arbitrary actions on the user's behalf.

acamill commented 2 years ago

@jon-chuang May I ask what is the use case that this would solve? I find that there is often detour solution for this kind of issues, for example Pancake swap auto compound with a faucet to trigger things with an incentive.

jon-chuang commented 2 years ago

Yeah, this can be easily replaced with a crank. Many liquidity mining reward payouts use cranks nowadays

arielsegura commented 2 years ago

Another use case for this would be recurring payments for subscriptions to digital services like Youtube or IRL services like the gym. It sounds like an overkill to me that everyone in the world that wants to accept a payment through solana, needs to have their own backend that triggers on-chain programs.