Emitted Transactions

Your Hook can do a lot more than just block or allow transactions!

Background

All changes made to the XRP Ledger must be the result of applying a valid transaction to the ledger. Thus if some change X is made then some transaction Y is responsible.

When designing the Hooks API we needed a way for Hooks to make changes to the ledger beyond simply accepting or rejecting a transaction. However attaching these changes to the Originating Transaction was confusing and resulted in a large increase in the general complexity of the system.

Suppose for example that a Hook needs to send you some funds... the send operation would be effectively enacted onto the ledger by the Originating Transaction which might have been something completely unrelated such as an Account Set transaction. Additionally this send operation would need to be able to potentially trigger another Hook on the receiving end of a payment.

The solution: Emitted Transactions. We allow the Originating Transaction to do exactly what the contents of the Transaction say it will do. If our Hook needs to make an additional change to the ledger such as sending a payment, it creates and then emits a brand new transaction.

What are Emitted Transactions?

Emitted Transactions are new transactions created by the execution of a Hook and entered into consensus for processing in the next ledger. The transaction may be of any Transaction Type but must follow strict emission rules.

To emit a transaction the Hook first prepares the serialized transaction then calls emit.

Because emitted transactions can trigger Hooks in the next ledger which in turn may emit more transactions, all emitted transactions carry a burden and a generation field in their EmitDetails block. The EmitDetails block replaces the signature field in a traditional transaction.

The burden and generation fields collectively prevent Fork bomb attacks on the ledger by exponentially increasing the cost of exponentially expanding emtited transactions.

It is important to note that the Hooks API follows the strict rule of no rewriting. You must present an emitted transaction in full, valid and canonically formed to xrpld for emission or it will be rejected. It is not xrpld's job to build your transaction for you. The Hook must do this itself.

Callbacks

As introduced in Introduction and Terminology emitted transactions trigger callbacks when they are accepted into a ledger. Due to the decentralised nature of consensus acceptance into a ledger of an emitted transaction is not a guarantee, although it is usually all-but guaranteed.

If an emitted transaction expires before it can be accepted into a ledger (for any number of reasons: the ledgers may be full, the fee may be too high for the emitted transaction or the emitted transaction may be somehow invalid) then a pseudo transaction is created in the ledger to clean up the emitted transaction. This pseudo transaction also calls the callback of your hook, with parameter = 1 to indicate the emitted transaction indeed failed.

Emission Rules

The emit Hook API will enforce the following rules on a proposed (to be emitted) transaction.

#Emission RuleExplanation
1sfSequence = 0Emitted Transactions do not increase the sequence number of the Hook Account. This must always be set to zero.
2sfPubSigningKey = 0Emitted Transactions are not signed but this is a required field for xrpld processing. It must be set to all zeros.
3sfEmitDetails present and validEmitted Transactions require an sfEmitDetails block and this must be correctly filled. See EmitDetails section below.
4sfSignature absentThis field must be absent in the emitted transaction because if it were not then the transaction would be ambiguous.
5LastLedgerSequence valid and in the futureAll emitted transactions must have a last ledger sequence set so that the Hook knows if the emitted transaction failed (since it did not get a callback in time). This is currently set to a maximum of 5 ledgers after the current ledger.
6FirstLedgerSequence valid and set to the next ledgerAll emitted transactions must have a first ledger sequence set to the next ledger (after the current ledger) so that Hooks do not recursively cascade within a single ledger. This is currently enforced to be the next ledger after the current ledger.
7Fee appropirately computed and setThe fee is dependent on the size of the emtited transaction and the burden on the network (i.e. whether this emitted transaction was the result of another emitted transaction.)
8Generation cap not exceededAn emitted transaction can produce other emitted transactions, and these can form a chain. The length of the chain is the sfEmitGeneration. This is currently capped at 10.

EmitDetails block

All emitted transactions must contain an sfEmitDetails object correctly populated with the fields in the table below.

FieldRequired ValueDescription
sfEmitGenerationIf the Originating Transaction was itself an emitted transaction then one more than the sfEmitGeneration of that transaction.

If the Originating Transaction was not an emitted transaction then 1.

This should be populated using etxn_generation.
This field keeps track of a chain of emitted transactions that in turn cause other transactions to be emitted.
sfEmitBurdenIf the Originating Transaction was itself an emitted transaction then the burden of the Originating Transaction multiplied by the maximum number of transactions the Hook has declared it will emit using etxn_reserve.

If the Originating Transaction was not an emitted transaction then 1.

This should be populated using etxn_burden.
This field is a heuristic for detecting forkbombs. Fees are based on burden and will increase exponentially when a chain reaction is started to prevent the network becoming overun by self-reinforcing emitted transactions.
sfEmitParentTxnIDThe transaction ID of the Originating TransactionThe Hook Execution that emitted the transaction is connected to the Originating Transaction. Therefore this field is always required for the efficient tracing of behaviour.
sfEmitNonceA special deterministic nonce produced by a call to nonceEmitted Transactions would be identical with the same fields and therefore have identical transaction hashes if a nonce were not used. However every node on the network needs to agree on the nonce, so a special Hook API to produce a deterministic nonce is made available.
sfEmitCallbackThe 20 byte Hook Account IDThis field is used by xrpld when it needs to intitate a callback, such that it knows which Hook and account to initate the callback on. Callbacks happen when an emitted transaction is accepted into a ledger.

👍

Check the examples

The Example Hooks, in particular Peggy, Carbon and Doubler, demonstrate how to emit both simple and more complicated transactions.


What’s Next