burrow icon indicating copy to clipboard operation
burrow copied to clipboard

[SNatives] Contract Call Scheduler

Open compleatang opened this issue 6 years ago • 2 comments

Blockchains are good at two things: time & ordering

  • @silasdavis

Currently in most blockchain designs we do not have an ability to schedule future processing. However there are many useful reasons why such a feature would be leveraged. This ticket summarizes the two key user stories for leveraging such a feature.

User Story A: Account Driven Scheduling

As a properly permissioned user of a burrow blockchain, I need to be able to send a transaction at time.Now() that is properly signed and has the transaction signature along the lines of destination, executionTime, data. Upon receiving and ensuring such a transaction is properly signed and permissioned, the nodes operating a burrow network should schedule this transaction for execution in the block which covers the unixTime placed in the executionTime with the original sender built into the transaction envelope.

User Story B: Contract Drive Scheduling

As a properly permissioned user of a burrow blockchain, I need to be able to deploy a contract that can interact with the scheduler in a similar manner to the user story listed above.

compleatang avatar Apr 03 '19 15:04 compleatang

I don't entirely follow some of the wording in story A, but I think I get the gist.

We discussed the ability to schedule transactions for the proposal mechanism, so that a proposal once ratified (enough votes) would be schedule for a future block. In the end we decide to immediately execute once ratified - so the transaction that ratified would actually trigger execution. In any case we could use a similar mechanism we discussed to schedule.

The way it could work is:

  • A transaction can be submitted to the network with a target height
  • Signatures etc and checked as normal, but the transaction is added to a tree index by the target height
  • At each EndBlock we access the tree at that height to see if we have any transactions schedules, if we do we iterate over them executing each one. It's possible some fail because sequence numbers or other state has changed. This is logged in state as an exceptional TxExecution

A few notes:

  • We can't use BroadcastSync anymore because it will wait too long and breach timeout. For a scheduled transaction we might explicitly return a receipt here. Later on the submitted would need to check the result with rpc/events.Tx
  • This is efficient because we only have one key/subtree to check each height
  • We don't need any additional secondary signatures or anything - we do consensus on the tx when it is submitted as normal, and it becomes part of authenticated state like anything else - it just lives in a special pending tree (and stays recorded there forever, by default)
  • We will end up with transactions in Burrow's state for a particular height that have no corresponding transaction in Tendermint state. Now we append them to the end of the block so that the indices for the 'real' transactions that block match up with Tendermint. But this could cause some bugs if we implement something that assumes they line up (in forensics for example).

Things are trickier if we want to make this work for time-based scheduling.

First of all we should be aware Tendermint's BFT time gets more unreliable the more nodes we have. It is at least guaranteed to go up. I'm not sure it will ever be suitable for really time-critical tasks (you'd hope it would get the day right, but it depends how much consistency you can get out of the other validators).

The other issue is the best we can do is to schedule the transaction to run at the end of executing the first block who's timestamp breaches the target time for the execution. We could operate with some specific delta so we run it a little earlier than target but this doesn't buy us anything. The bigger problem here is we no longer have empty blocks just for the hell of it - so to get a reasonable resolution we would need to set EmptyBlocks = 1m or something.

The final issue I can think of is that since we do not know which block height will associate with which time window, unlike with height-based scheduling we can no longer use such an efficient structure to schedule blocks. We will have to replace a lookup with a range search. We can certainly support this but it is trickier.

Supporting just height-based scheduling would avoid some of these, but with irregular block timings (no empty blocks) it is now even more divergent from real time. Still useful for governance though I think, where you can at least put a lower bounds on the time until a new regime comes into force.

....

It sounds like you may have had in mind that you schedule transactions 'subjectively' as in do not put them through consensus but instead let a burrow node 'hang on to them' and have it send them when the time came. This might work, but we are not set up to store subjective non-global state in a crash-fault-tolerant manner. We would also be subject to network congestion in terms of getting the transactions into a particular block (whereas in the model above the execution machinery would guarantee them getting into their scheduled block).

silasdavis avatar Aug 02 '19 12:08 silasdavis

I think this still represents useful functionality for Burrow to host

silasdavis avatar Feb 01 '21 12:02 silasdavis