|
| 1 | +# Scheduled transactions |
| 2 | + |
| 3 | +An ordinary transaction must be submitted to the network with the signatures |
| 4 | +of enough Ed25519 keys to activate all the Hedera keys required to sign it. |
| 5 | +Otherwise it will resolve to `INVALID_SIGNATURE`. |
| 6 | + |
| 7 | +Here we describe a new kind of _scheduled_ transaction that is not directly |
| 8 | +submitted to the network, but rather created as part of a _schedule entity_ in |
| 9 | +the network's action queue. An Ed25519 key "signs" a scheduled transaction |
| 10 | +by signing an ordinary transaction that either creates or affirms the |
| 11 | +schedule entity. |
| 12 | + |
| 13 | +Along with a scheduled transaction, a schedule entity (or simply _schedule_) also contains, |
| 14 | + 1. An optional memo. |
| 15 | + 2. An optional admin key that can be used to delete the schedule. |
| 16 | + 3. An optional account to be charged the service fee for the scheduled transaction. |
| 17 | + 4. A list of the Ed25519 keys that the network deems to have signed the scheduled transaction. |
| 18 | + |
| 19 | +The schedule entity type is managed by four new HAPI operations, |
| 20 | + 1. The `ScheduleCreate` transaction creates a new schedule (possibly with a non-empty list of signing keys). |
| 21 | + 2. The `ScheduleSign` transaction adds one or more Ed25519 keys to a schedule's list of affirmed signers. |
| 22 | + 3. The `ScheduleDelete` transaction marks a schedule as deleted; its transaction will not be executed. |
| 23 | + 4. The `GetScheduleInfo` query gets the current state of a schedule. |
| 24 | + |
| 25 | +It is important to understand that the bytes of the inner scheduled transaction are never |
| 26 | +directly signed by an Ed25519 key. Only `ScheduleCreate` or `ScheduleSign` bytes |
| 27 | +are ever signed, in the ordinary way. |
| 28 | + |
| 29 | +## Ready-to-execute status |
| 30 | + |
| 31 | +A scheduled transaction is _ready-to-execute_ if its schedule has a list of |
| 32 | +Ed25519 signing keys which, taken together, meet the _signing requirements_ of all the |
| 33 | +Hedera keys _prerequisite_ to the scheduled transaction. (See the |
| 34 | +[next section](#prerequisite-signing-keys) for more discussion on prerequisite keys.) |
| 35 | + |
| 36 | +Because Hedera keys can include key lists and threshold keys, sometimes there are |
| 37 | +many different lists of Ed25519 signing keys which could meet a Hedera key's signing |
| 38 | +requirements. |
| 39 | + |
| 40 | +For example, suppose Alice has an account whose key is a 1-of-3 threshold made up of |
| 41 | +Ed25519 keys `KeyA`, `KeyB`, and `KeyC`. As long as any of `KeyA`, `KeyB`, or `KeyC` have signed, |
| 42 | +the signing requirement for the Hedera key on Alice's account is met. |
| 43 | + |
| 44 | +(We often say a Hedera key has "signed a transaction" when enough Ed25519 keys |
| 45 | +have signed to meet the Hedera key's signing requirements; but notice this is |
| 46 | +a bit imprecise---only Ed25519 keys ever "sign" in the cryptographic sense.) |
| 47 | + |
| 48 | +### Prerequisite signing keys |
| 49 | + |
| 50 | +First, if a schedule lists an account to be charged the service fee for |
| 51 | +its scheduled transaction, the Hedera key of that account is prerequisite |
| 52 | +to the scheduled transaction. |
| 53 | + |
| 54 | +Second, if some non-payer Hedera key would need to sign a scheduled transaction |
| 55 | +if it was submitted directly to the network, that non-payer key is prerequisite |
| 56 | +to the scheduled transaction. Consider three examples. |
| 57 | + 1. A scheduled `CryptoTransfer` of 1ℏ from account `0.0.X` to account `0.0.Y`, |
| 58 | + which has `receiverSigRequired=true`. The keys on accounts `0.0.X` and |
| 59 | + `0.0.Y` are both prerequisite. |
| 60 | + 2. A scheduled `SubmitMessage` to a topic `0.0.Z` which has a submit key. The |
| 61 | + submit key on topic `0.0.Z` is prerequisite. |
| 62 | + 3. A scheduled `TokenMint` for a token `0.0.T` with a supply key. The supply |
| 63 | + key on token `0.0.T` is prerequisite. (Although `TokenMint` is |
| 64 | + not currently in the [scheduling whitelist](https://github.com/hashgraph/hedera-services/blob/master/hedera-node/src/main/resources/bootstrap.properties#L64), |
| 65 | + eventually all transaction types will be.) |
| 66 | + |
| 67 | +## Triggered execution |
| 68 | + |
| 69 | +A schedule is _triggered_ when the network handles a `ScheduleCreate` |
| 70 | +or `ScheduleSign` that adds enough signing keys to make its scheduled |
| 71 | +transaction ready-to-execute. (You may create a schedule whose initial |
| 72 | +signing keys are already triggering.) A scheduled transaction executes |
| 73 | +immediately after its schedule is triggered. |
| 74 | + |
| 75 | +It is crucial to understand that in Hedera Services 0.13.0, a scheduled |
| 76 | +transaction **only** executes when its schedule is triggered by a |
| 77 | +`ScheduleCreate` or `ScheduleSign`! The network does not proactively monitor |
| 78 | +how changes to Hedera keys may impact the ready-to-execute status of |
| 79 | +existing scheduled transactions. |
| 80 | + |
| 81 | +For example, suppose the Hedera key on account `0.0.X` is a key list of two |
| 82 | +Ed25519 keys `KeyA` and `KeyB`; and is the sole prerequisite to a scheduled transaction |
| 83 | +whose schedule `0.0.S` already lists `A` as a signer. Say we now update account |
| 84 | +`0.0.X` to remove `KeyB` from its key. Schedule `0.0.S` is **not** automatically |
| 85 | +triggered! We need to submit a `ScheduleSign` for `0.0.S` to trigger it. (This |
| 86 | +`ScheduleSign` can be signed by just a payer key, since we made the scheduled |
| 87 | +transaction ready-to-execute by weakening the signing requirement for the key |
| 88 | +on account `0.0.X`.) |
| 89 | + |
| 90 | +## The `ScheduleCreate` transaction |
| 91 | + |
| 92 | +``` |
| 93 | +message ScheduleCreateTransactionBody { |
| 94 | + SchedulableTransactionBody scheduledTransactionBody = 1; // The scheduled transaction |
| 95 | + string memo = 2; // An optional memo with a UTF-8 encoding of no more than 100 bytes |
| 96 | + Key adminKey = 3; // An optional Hedera key which can be used to sign a `ScheduleDelete` and make the schedule un-triggerable |
| 97 | + AccountID payerAccountID = 4; // An optional id of the account to be charged the service fee for the scheduled transaction at the consensus time that it executes (if ever); defaults to the `ScheduleCreate` payer if not given |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +The new `SchedulableTransactionBody` message is a strict subset of the `TransactionBody` message which omits the |
| 102 | +top-level `TransactionID`, `nodeAccountID`, and `transactionValidDuration` fields; and does not allow the |
| 103 | +`ScheduleCreateTransactionBody` and `ScheduleSignTransactionBody` messages in its `data` element. Any |
| 104 | +unknown fields in the submitted `SchedulableTransactionBody` will be ignored, although they _will_ affect |
| 105 | +the "identity" of the schedule (see [below](#receipts-and-duplicate-creations)). |
| 106 | + |
| 107 | +As with all other entity types, a schedule remains in network state until it expires, even if its scheduled |
| 108 | +transaction has already been executed or it has been marked deleted. |
| 109 | + |
| 110 | +### Paying for scheduled transactions |
| 111 | + |
| 112 | +If the `ScheduleCreate` gives a `payerAccountID`, the network will charge this payer the service fee |
| 113 | +for the scheduled transaction at the consensus time that it executes (if ever). If no such payer is specified, |
| 114 | +the network will charge the payer of the originating `ScheduleCreate`. |
| 115 | + |
| 116 | +### Receipts and duplicate creations |
| 117 | + |
| 118 | +When a `ScheduleCreate` resolves to `SUCCESS`, its receipt includes a new `ScheduleID` field |
| 119 | +with the id of the created schedule. Its receipt _also_ includes a new |
| 120 | +`scheduledTransactionID` field with the `TransactionID` that can be used to query for the |
| 121 | +record of the scheduled transaction's execution. |
| 122 | + |
| 123 | +This `scheduledTransactionID` will be the `TransactionID` of the `ScheduleCreate`, |
| 124 | +with a new additional field `scheduled=true`. However, clients should always use the id |
| 125 | +from the receipt instead of relying on this correspondence. |
| 126 | + |
| 127 | +#### Duplicate creations |
| 128 | +There is a special case in which a `ScheduleCreate` transaction tries to re-create a |
| 129 | +schedule that already exists in state. When this happens, the `ScheduleCreate` resolves |
| 130 | +to a new status of `IDENTICAL_SCHEDULE_ALREADY_CREATED`, and the `ScheduleID` in its receipt points |
| 131 | +to the existing schedule. (It also contains the `TransactionID` used to query for the |
| 132 | +record of the existing scheduled transaction.) A client receiving `IDENTICAL_SCHEDULE_ALREADY_CREATED` |
| 133 | +can then submit a `ScheduleSign` (see below) with the given `ScheduleID`, signing with |
| 134 | +the same Ed25519 keys it used for its own create attempt. |
| 135 | + |
| 136 | +Two <tt>ScheduleCreate</tt> transactions are <i>identical</i> if they are equal in all their fields |
| 137 | +other than, possibly, <tt>payerAccountID</tt>. (Here "equal" should be understood in the sense of |
| 138 | +gRPC object equality in the network software runtime. In particular, a gRPC object with |
| 139 | +[unknown fields](https://developers.google.com/protocol-buffers/docs/proto3#unknowns) |
| 140 | +is not equal to a gRPC object without unknown fields, even if they agree on all known fields.) |
| 141 | + |
| 142 | +### Enforced checks and the scheduling whitelist |
| 143 | + |
| 144 | +The only body-specific precheck enforced for a `ScheduleCreate` transaction is that the |
| 145 | +`memo` field is valid. At consensus, the following checks are enforced: |
| 146 | + 1. The `memo` must be valid. |
| 147 | + 2. The type of the `scheduledTransactionBody` must be in the `scheduling.whitelist`; and it |
| 148 | + must reference only non-deleted entities that exist in state at the time the `ScheduleCreate` |
| 149 | + reaches consensus. |
| 150 | + 3. The `adminKey` must be valid, if present. |
| 151 | + |
| 152 | +## The `ScheduleSign` transaction |
| 153 | + |
| 154 | +``` |
| 155 | +message ScheduleSignTransactionBody { |
| 156 | + ScheduleID scheduleID = 1; // The id of an existing schedule to affirm signing keys for |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +Suppose we use the `scheduleID` field to point to schedule `0.0.12345`, which contains a |
| 161 | +scheduled transaction `X`. If any of the Ed25519 keys required to sign `X` sign |
| 162 | +our `ScheduleSign` ordinary transaction, then the network will update the state of |
| 163 | +schedule `0.0.12345` with these keys. |
| 164 | + |
| 165 | +(It is permissible, though pointless, to sign the `ScheduleSign` with keys not |
| 166 | +required for the execution of `X`.) |
| 167 | + |
| 168 | +### Receipts |
| 169 | + |
| 170 | +When a `ScheduleSign` resolves to `SUCCESS`, its receipt includes a new |
| 171 | +`scheduledTransactionID` field which is the `TransactionID` that can be used to query |
| 172 | +for the record of the execution of the scheduled transaction in the signed schedule. |
| 173 | + |
| 174 | +## The `ScheduleDelete` transaction |
| 175 | + |
| 176 | +``` |
| 177 | +message ScheduleDeleteTransactionBody { |
| 178 | + ScheduleID scheduleID = 1; // The id of an existing schedule to delete |
| 179 | +} |
| 180 | +``` |
| 181 | + |
| 182 | +When a `ScheduleDelete` resolves to `SUCCESS`, its target schedule is marked as |
| 183 | +as deleted. Any future attempts to trigger this schedule will result in `SCHEDULE_WAS_DELETED`. |
| 184 | +However, the schedule will remain in network state until it expires. (Note that if we try |
| 185 | +to delete a schedule that already executed, our `ScheduleDelete` will resolve |
| 186 | +to `SCHEDULE_WAS_EXECUTED` and have no effect.) |
| 187 | + |
| 188 | +## The `ScheduleGetInfo` query |
| 189 | + |
| 190 | +``` |
| 191 | +message ScheduleGetInfoQuery { |
| 192 | + QueryHeader header = 1; // Standard query metadata, including payment |
| 193 | + ScheduleID schedule = 2; // The id of an existing schedule |
| 194 | +} |
| 195 | + |
| 196 | +message ScheduleGetInfoResponse { |
| 197 | + ScheduleID scheduleID = 1; // The id of the schedule |
| 198 | + Timestamp deletionTime = 2; // If the schedule has been deleted, the consensus time when this occurred |
| 199 | + Timestamp executionTime = 3; // If the schedule has been executed, the consensus time when this occurred |
| 200 | + Timestamp expirationTime = 4; // The time at which the schedule will expire |
| 201 | + SchedulableTransactionBody scheduledTransactionBody = 5; // The scheduled transaction |
| 202 | + string memo = 6; // The publicly visible memo of the schedule |
| 203 | + Key adminKey = 7; // The key used to delete the schedule from state |
| 204 | + KeyList signers = 8; // The Ed25519 keys the network deems to have signed the scheduled transaction |
| 205 | + AccountID creatorAccountID = 9; // The id of the account that created the schedule |
| 206 | + AccountID payerAccountID = 10; // The id of the account responsible for the service fee of the scheduled transaction |
| 207 | + TransactionID scheduledTransactionID = 11; // The transaction id that will be used in the record of the scheduled transaction (if it executes) |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +## Records of scheduled transactions |
| 212 | + |
| 213 | +Just as an ordinary transaction, a scheduled transaction that achieves execution has a unique consensus timestamp. |
| 214 | +It also has a record in the record stream. However, there _are_ two items that distinguish the record of a |
| 215 | +scheduled transaction. |
| 216 | + 1. Its `TransactionID` will contain the new `scheduled=true` flag. |
| 217 | + 2. It will have a new `scheduleRef` field with the `ScheduleID` of the schedule that managed its execution. |
0 commit comments