diff --git a/docs/protocol/src/SUMMARY.md b/docs/protocol/src/SUMMARY.md index 54d89638d6..3a47451e64 100644 --- a/docs/protocol/src/SUMMARY.md +++ b/docs/protocol/src/SUMMARY.md @@ -35,7 +35,6 @@ - [Distributed Key Generation](./crypto/flow-encryption/dkg.md) - [Homomorphic Threshold Encryption](./crypto/flow-encryption/threshold-encryption.md) - [Flow Encryption and Consensus](./protocol/flow-consensus.md) - - [Transaction Signing](./crypto/transaction_signing.md) - [Groth 16 Setup Ceremony](./setup.md) - [Groth16 Recap](./setup/groth16_recap.md) - [Discrete Logarithm Proofs](./setup/dlog_proofs.md) @@ -48,8 +47,11 @@ - [State Commitment Tree](./sct.md) - [Tiered Commitment Tree](./sct/tct.md) - [Nullifiers](./protocol/notes/nullifiers.md) +- [Asset Model](./assets.md) +- [Transaction Model](./transactions.md) + - [Transaction Signing](./transactions/signing.md) + - [Action Reference](./transactions/actions.md) - [Multi-Asset Shielded Pool](./shielded_pool.md) - - [Asset IDs and Value Commitments](./protocol/value_commitments.md) - [Note Plaintexts](./protocol/notes/note_plaintexts.md) - [Note Commitments](./protocol/notes/note_commitments.md) - [Note Ciphertexts](./protocol/notes/note_ciphertexts.md) diff --git a/docs/protocol/src/protocol/value_commitments.md b/docs/protocol/src/assets.md similarity index 54% rename from docs/protocol/src/protocol/value_commitments.md rename to docs/protocol/src/assets.md index 74d1882a31..67fd3335f0 100644 --- a/docs/protocol/src/protocol/value_commitments.md +++ b/docs/protocol/src/assets.md @@ -1,10 +1,10 @@ -# Asset IDs and Value Commitments +# Asset Model -Penumbra's shielded pool can record arbitrary assets. These assets either -originate on Penumbra itself, or, more commonly, originate on other -IBC-connected chains. To record arbitrary assets and enforce value balance -between them, we draw on [ideas][multi_asset] originally proposed for Zcash and -adapt them to the Cosmos context. +Penumbra can record arbitrary assets. These assets either originate on Penumbra +itself, or, more commonly, originate on other IBC-connected chains. To record +arbitrary assets and enforce value balance between them, we draw on +[ideas][multi_asset] originally proposed for Zcash and adapt them to the Cosmos +context. ## Asset types and asset IDs @@ -14,8 +14,9 @@ To be precise, we define: - an *asset ID* to be an $\mathbb F_q$ element; - a *value* to be a typed quantity, i.e., an amount and an asset ID. -Some asset IDs correspond to a *denomination*, an [ADR001]-style denomination -trace uniquely identifying a cross-chain asset and its provenance, such as: +All asset IDs are currently computed as the hash of a *denomination*, an +[ADR001]-style denomination trace uniquely identifying a cross-chain asset and +its provenance, such as: - `denom` (native chain A asset) - `transfer/channelToA/denom` (chain B representation of chain A asset) - `transfer/channelToB/transfer/channelToA/denom` (chain C representation of chain B representation of chain A asset) @@ -31,12 +32,21 @@ personalization `label` on input `input`. Then asset IDs are computed as asset_id = from_le_bytes(hash(b"Penumbra_AssetID", asset_type)) mod q ``` -Other asset IDs do not correspond to denominations, but are computed as hashes -of other state data. By making the asset ID itself be a hash of extended state -data, a note recording value of that type also binds to that extended data, even -though it has the same size as any other note. For instance: -- [The ZSwap mechanism](../zswap/swap.md) has the `Swap` action form a *Swap NFT*, whose asset ID is a Poseidon hash of the user's secret input values address, so that when the `SwapClaim` action spends the NFT, it can (zk)prove consistency of the newly minted swap outputs with the user's inputs and the chain's clearing prices, and that the newly minted outputs are sent to the user's own address. -- [The LPNFT mechanism](../zswap/lpnft.md) creates asset IDs that bind both a liquidity position ID and the position state, so that the value balance mechanism described below can track state changes to concentrated liquidity positions. +In the future, Penumbra may have other asset IDs do not correspond to +denominations, but are computed as hashes of other state data. By making the +asset ID itself be a hash of extended state data, a note recording value of that +type also binds to that extended data, even though it has the same size as any +other note. Currently, however, all asset IDs are computed as the hashes of +denomination strings. + +## Asset Metadata + +Penumbra also supports Cosmos-style `Metadata` for assets. The chain maintains +an on-chain lookup table of asset IDs to asset metadata, but the on-chain +metadata is minimal and generally only includes the denomination string. Client +software is expected to be opinionated about asset metadata, supplying +definitions with symbols, logos, etc. to help users understand the assets they +hold. ## Value Generators @@ -74,25 +84,6 @@ Alternatively, this can be thought of as a commitment to a (sparse) vector recording the amount of every possible asset type, almost all of whose coefficients are zero. -## Binding Signatures - -Finally, we'd like to be able to prove that a certain value commitment $C$ is a -commitment to $0$. One way to do this would be to prove knowledge of an opening -to the commitment, i.e., producing $\widetilde{v}$ such that $$C = [\widetilde{v}] \widetilde{V} = \operatorname{Commit}(0, \widetilde{v}).$$ But this is exactly what it means to -create a Schnorr signature for the verification key $C$, because a Schnorr -signature is a proof of knowledge of the signing key in the context of the -message. - -Therefore, we can prove that a value commitment is a commitment to $0$ by -treating it as a `decaf377-rdsa` verification key and using the corresponding -signing key (the blinding factor) to sign a message. This also gives a way to -bind value commitments to a particular context (e.g., a transaction), by using -the context as the message to be signed, in order to, e.g., ensure that value -commitments cannot be replayed across transactions. - - - - [multi_asset]: https://github.com/zcash/zips/blob/626ea6ed78863290371a4e8bc74ccf8e92292099/drafts/zip-user-defined-assets.rst [ADR001]: https://docs.cosmos.network/master/architecture/adr-001-coin-source-tracing.html [IBC]: https://docs.cosmos.network/master/ibc/overview.html diff --git a/docs/protocol/src/crypto/transaction_signing.md b/docs/protocol/src/crypto/transaction_signing.md index 07459a1278..c6f0eafecc 100644 --- a/docs/protocol/src/crypto/transaction_signing.md +++ b/docs/protocol/src/crypto/transaction_signing.md @@ -1,151 +1 @@ # Transaction Signing - -In a transparent blockchain, a signer can inspect the transaction to be signed to -validate the contents are what the signer expects. However, in a shielded -blockchain, the contents of the transaction are opaque. Ideally, using a private -blockchain would enable a user to sign a transaction while also understanding -what they are signing. - -To avoid the blind signing problem, in the Penumbra protocol we allow the user -to review a description of the transaction - the `TransactionPlan` - prior to -signing. The `TransactionPlan` contains a declarative description of all details of the proposed transaction, including a plan of each action in a transparent -form, the fee specified, the chain ID, and so on. From this plan, we authorize the and build the transaction. This has the additional advantage of allowing the signer to authorize the -transaction while the computationally-intensive Zero-Knowledge -Proofs (ZKPs) are optimistically generated as part of the transaction build process. - -The signing process first takes a `TransactionPlan` and `SpendKey` and returns -the `AuthorizationData`, essentially a bundle of signatures over -the *effect hash*, which can be computed directly from the plan data. You can -read more about the details of the effect hash computation below. - -The building process takes the `TransactionPlan`, generates the proofs, and constructs a fully- -formed `Transaction`. This process is internally partitioned into three steps: -1. Each `Action` is individually built based to its specification in the `TransactionPlan`. -2. The pre-built actions to collectively used to construct a transaction with placeholder dummy signatures, that can be filled in once the -signatures from the `AuthorizationData` are ready[^1]. This intermediate state -of the transaction without the full authorizing data is referred to as the "**Unauthenticated Transaction**". -1. Slot the `AuthorizationData` to replace the placeholder signatures to assemble the final `Transaction`. - -The Penumbra protocol was designed to only require the custodian, e.g. the hardware wallet -environment, to do signing, as the generation of ZKPs can be done without access to signing keys, requiring only witness data and viewing keys. - -A figure showing how these pieces fit together is shown below: - -``` -╔════════════════════════╗ -║ Authorization ║ -║ ║ -║┌──────────────────────┐║ -║│ Spend authorization │║ -║│ key │║ ┌───────────────────┐ -║└──────────────────────┘║ │ │ -║ ║───▶│ AuthorizationData │──┐ -║ ║ │ │ │ -║┌──────────────────────┐║ └───────────────────┘ │ -║│ EffectHash │║ │ -║└──────────────────────┘║ │ -║ ║ │ -║ ║ │ -╚════════════▲═══════════╝ │ - │ │ - │ │ ┌───────────┐ - ┌───────────┴───────────┐ │ │ │ - │ │ └─────────┬────▶│Transaction│ - │ TransactionPlan │ | │ │ - │ │ │ └───────────┘ - └───────────┬───────────┘ │ - │ │ - │ │ - │ │ - ╔═══════════▼════════════╗ │ - ║ Proving ║ │ - ║ ║ │ - ║┌──────────────────────┐║ │ - ║│ WitnessData │║ │ - ║└──────────────────────┘║ ┌──────────────────────────────┐ │ - ║ ║ │ │ │ - ║ ╠──▶│ Unauthenticated Transaction ├─┘ - ║┌──────────────────────┐║ │ │ - ║│ Full viewing key │║ └──────────────────────────────┘ - ║└──────────────────────┘║ - ║ ║ - ║ ║ - ║ ║ - ╚════════════════════════╝ - -``` - -Transactions are signed used the [`decaf377-rdsa` construction](../crypto/decaf377-rdsa.md). As described briefly in that section, there are two signature domains used in Penumbra: `SpendAuth` signatures and `Binding` signatures. - -## `SpendAuth` Signatures - -`SpendAuth` signatures are included on each `Spend` and `DelegatorVote` action -(see [Multi-Asset Shielded Pool](../shielded_pool.md) and [Governance](../governance.md) -for more details on `Spend` and `DelegatorVote` actions respectively). - -The `SpendAuth` signatures are created using a randomized signing key $rsk$ and the corresponding randomized verification key $rk$ provided on the action. The purpose of the randomization is to prevent linkage of verification keys across actions. - -The `SpendAuth` signature is computed using the `decaf377-rdsa` `Sign` algorithm -where the message to be signed is the *effect hash* of the entire transaction -(described below), and the `decaf377-rdsa` domain is `SpendAuth`. - -## Effect Hash - -The effect hash is computed over the *effecting data* of the transaction, which following -the terminology used in Zcash[^2]: - -> "Effecting data" is any data within a transaction that contributes to the effects of applying the transaction to the global state (results in previously-spendable coins or notes becoming spent, creates newly-spendable coins or notes, causes the root of a commitment tree to change, etc.). - -The data that is _not_ effecting data is *authorizing data*: - ->"Authorizing data" is the rest of the data within a transaction. It does not contribute to the effects of the transaction on global state, but allows those effects to take place. This data can be changed arbitrarily without resulting in a different transaction (but the changes may alter whether the transaction is allowed to be applied or not). - -For example, the nullifier on a `Spend` is effecting data, whereas the -proofs or signatures associated with the `Spend` are authorizing data. - -In Penumbra, the effect hash of each transaction is computed using the BLAKE2b-512 -hash function. The effect hash is derived from the proto-encoding of the action - in -cases where the effecting data and authorizing data are the same, or the *body* -of the action - in cases where the effecting data and authorizing data are different. -Each proto has a unique string associated with it called its *Type URL*, -which is included in the inputs to BLAKE2b-512. -Type URLs are variable length, so a fixed-length field (8 bytes) is first included -in the hash to denote the length of the Type URL field. - -Summarizing the above, the effect hash _for each action_ is computed as: - -``` -effect_hash = BLAKE2b-512(len(type_url) || type_url || proto_encode(proto)) -``` - -where `type_url` is the bytes of the variable-length Type URL, `len(type_url)` is the length of the Type URL encoded as 8 -bytes in little-endian byte order, `proto` represents the proto used to represent -the effecting data, and `proto_encode` represents encoding the proto message as -a vector of bytes. In Rust, the Type URL is found by calling [`type_url()` on the protobuf -message](https://docs.rs/prost/latest/prost/trait.Name.html#method.type_url). - -All transaction data field effect hashes, such as the `Fee`, `MemoCiphertext`, and `TransactionParameters`, as well as the per-action effect hashes, are computed using this method. - -### Transaction Effect Hash - -To compute the effect hash of the _entire transaction_, we combine the hashes of the individual fields in the transaction body. First we include the fixed-sized effect hashes of the per-transaction data fields: the transaction parameters `eh(tx_params)`, fee `eh(fee)`, (optional) detection data `eh(detection_data)`, and (optional) memo `eh(memo)` which are derived as described above. Then, we include the number of actions $j$ and the fixed-size effect hash of each action `a_0` through `a_j`. Combining all fields: - -``` -effect_hash = BLAKE2b-512(len(type_url) || type_url || eh(tx_params) || eh(fee) || eh(memo) || eh(detection_data) || j || eh(a_0) || ... || eh(a_j)) -``` - -where the `type_url` is the variable-length Type URL of the transaction body message, and `len(type_url)` is the length of that string encoded as 8 bytes in little-endian byte order. - -## `Binding` Signature - -The `Binding` signature is computed once on the transaction level. -It is a signature over a hash of the authorizing data of that transaction, called the *auth hash*. The auth hash of each transaction is computed using the BLAKE2b-512 hash function over the proto-encoding of the _entire_ `TransactionBody`. - -The `Binding` signature is computed using the `decaf377-rdsa` `Sign` algorithm -where the message to be signed is the *auth hash* as described above, and the -`decaf377-rdsa` domain is `Binding`. The binding signing key is computed using the random blinding factors for each balance commitment. - -[^1]: At this final stage we also generate the last signature: the binding signature, which can only be added -once the rest of the transaction is ready since it is computed over the proto-encoded `TransactionBody`. - -[^2]: https://github.com/zcash/zips/issues/651 diff --git a/docs/protocol/src/transactions.md b/docs/protocol/src/transactions.md new file mode 100644 index 0000000000..2c38aa735c --- /dev/null +++ b/docs/protocol/src/transactions.md @@ -0,0 +1,49 @@ +# Transaction Model + +A Penumbra transaction is a bundle of _actions_ that effect changes to the chain +state, together with additional data controlling how those actions are executed +or providing additional metadata. All actions in the transaction are executed together, and the transaction succeeds or fails atomically. A transaction body has four parts: + +1. A list of actions effecting changes to the chain state; +2. A set of `TransactionParameters` describing conditions under which the actions may be executed; +3. A set of `DetectionData` that helps third-party servers detect transactions using [Fuzzy Message Detection](./crypto/fmd.md); +4. A `MemoCiphertext` with an encrypted memo visible only to the sender and receiver(s) of the transaction. + +The [Transaction Signing](./transactions/signing.md) section describes transaction authorization. + +## Actions and Value Balance + +The primary content of a transaction is its list of actions. Each action is executed in sequence, and effects changes to the chain state. + +Crucially, each action makes a shielded contribution to the transaction's value +balance by means of a _balance commitment_, using the commitment scheme for asset values described in detail in the [Asset Model](./assets.md). + +Some actions, like a `Spend`, consume state fragments from the chain and release +value into the transaction, while others, like `Output`, consume value from the +transaction and record it in the state. And actions like `Delegate` consume one +type of value (the staking token) and release another type of value (the +delegation token). + +The chain requires that transactions do not create or destroy value. To +accomplish conservation of value, the _binding signature_ proves that the +transaction's value balance, summed up over all actions, is zero. This +construction works as follows. We'd like to be able to prove that a certain +value commitment $C$ is a commitment to $0$. One way to do this would be to +prove knowledge of an opening to the commitment, i.e., producing $\widetilde{v}$ +such that $$C = [\widetilde{v}] \widetilde{V} = \operatorname{Commit}(0, +\widetilde{v}).$$ But this is exactly what it means to create a Schnorr +signature for the verification key $C$, because a Schnorr signature is a proof +of knowledge of the signing key in the context of the message. + +Therefore, we can prove that a value commitment is a commitment to $0$ by +treating it as a `decaf377-rdsa` verification key and using the corresponding +signing key (the blinding factor) to sign a message. This also gives a way to +bind value commitments to a particular context (e.g., a transaction), by using a +hash of the transaction as the message to be signed, ensuring that actions +cannot be replayed across transactions without knowledge of their contents. + +The balance commitment mechanism is essential to security. It is what "glues" +the validity of the local state transitions provided by each action's proof +statement into validity of the global state transition. + +A complete list of actions and a summary of their effects on the chain state and the transaction's value balance is provided in the [Action Reference](./transactions/actions.md). diff --git a/docs/protocol/src/transactions/actions.md b/docs/protocol/src/transactions/actions.md new file mode 100644 index 0000000000..6b46756bce --- /dev/null +++ b/docs/protocol/src/transactions/actions.md @@ -0,0 +1,49 @@ +# Action Reference + +This page is a quick-reference list of transaction actions. Not all actions have proof statements, as only some actions perfom shielded state changes. Actions with proof statements are additionally listed separately for reference. + +## Actions by Proof Statement + +| Proof | Action | +| ----- | ------ | +| Spend | [`core.component.shielded_pool.v1.Spend`](../protocol/action_descriptions/spend.md) | +| Output | [`core.component.shielded_pool.v1.Output`](../protocol/action_descriptions/output.md) | +| Convert | [`core.component.stake.v1.UndelegateClaim`](../protocol/action_descriptions/undelegate_claim.md) | +| Delegator Vote | [`core.component.governance.v1.DelegatorVote`](../protocol/action_descriptions/delegator_vote.md) | +| Swap | [`core.component.dex.v1.Swap`](../protocol/action_descriptions/swap.md) | +| Swap Claim | [`core.component.dex.v1.SwapClaim`](../protocol/action_descriptions/swap_claim.md) | +| Nullifier Derivation | Not used in actions, intended for verifiable transaction perspectives | + +## All Actions + +This table lists all actions, their high-level purpose, and their contributions +to the transaction's value balance. For ease of comprehension, shielded and +transparent contributions to the transaction's value balance are listed +separately, though they are handled by the same mechanism: the chain forms +commitments with a zero blinding factor to accumulate transparent and shielded +contributions together. + +| Action | Description | Shielded Balance | Transparent Balance | +| ------ | ----------- | ------------- | -- | +| [`shielded_pool.v1.Spend`](../protocol/action_descriptions/spend.md) | Spends a note previously included on-chain, releasing its value into the transaction | $+$ (value of spent note) | | +| [`shielded_pool.v1.Output`](../protocol/action_descriptions/output.md) | Produces a new note controlled by a specified address and adds it to the chain state | $-$ (value of new note) | | +| [`dex.v1.Swap`](../protocol/action_descriptions/swap.md) | Submits a swap intent to the chain for batch execution | $-$ (prepaid claim fee) | $-$ (swap inputs) +| [`dex.v1.SwapClaim`](../protocol/action_descriptions/swap_claim.md) | Claims the outputs of a swap once the clearing price is known, producing new output notes directly | | $+$ (prepaid claim fee) | +| `stake.v1.ValidatorDefinition` | Uploads a new validator definition to the chain | | | +| `stake.v1.Delegate` | Delegates stake to a validator, exchanging the staking token for that validator's delegation token | | $-$ (staking token) $+$ (delegation token) +| `stake.v1.Undelegate` | Undelegates stake from a validator, exchanging delegation tokens for unbonding tokens | | $-$ (delegation token) $+$ (unbonding token) | +| `stake.v1.UndelegateClaim` | Converts unbonding tokens to staking tokens after unbonding completes, at a 1:1 rate unless there are slashing penalties | $-$ (unbonding token) $+$ (staking token) | +| `ibc.v1.IbcRelay` | Relays IBC messages from a counterparty chain | | | +| `ibc.v1.Ics20Withdrawal` | Initiates an outbound ICS-20 token transfer | | $-$ (transfer amount) | +| `dex.v1.PositionOpen` | Opens a liquidity position | | $-$ (initial reserves) $+$ (opened LPNFT) | +| `dex.v1.PositionClose` | Closes a liquidity position | | $-$ (opened LPNFT) $+$ (closed LPNFT) | +| `dex.v1.PositionWithdraw` | Withdraws reserves or rewards from a liquidity position, with sequence number $n$ | | $-$ (withdrawn seq $n-1$ LPNFT) $+$ (withdrawn seq $n$ LPNFT) $+$ (current position reserves) | +| `dex.v1.PositionRewardClaim` | Deprecated and unused | | | +| `governance.v1.ProposalSubmit` | Submits a governance proposal for voting | TODO | TODO | +| `governance.v1.ProposalWithdraw` | Withdraws a governance proposal from voting | TODO | TODO | +| `governance.v1.ValidatorVote` | Performs a governance vote as a validator | TODO | TODO | +| `governance.v1.DelegatorVote` | Performs a governance vote as a delegator | TODO | TODO | +| `governance.v1.ProposalDepositClaim` | Claims a proposal deposit once voting has finished | TODO | TODO | +| `governance.v1.CommunityPoolSpend` | Spends funds from the community pool | | $+$ (spent value) | +| `governance.v1.CommunityPoolOutput` | Like `Output`, but transparent | | $-$ (value of new note) +| `governance.v1.CommunityPoolDeposit` | Allows deposits into the community pool | | $-$ (value of deposit) | \ No newline at end of file diff --git a/docs/protocol/src/transactions/hashes.md b/docs/protocol/src/transactions/hashes.md new file mode 100644 index 0000000000..5b4b1f66b6 --- /dev/null +++ b/docs/protocol/src/transactions/hashes.md @@ -0,0 +1 @@ +# Authorization and Effect Hashes diff --git a/docs/protocol/src/transactions/signing.md b/docs/protocol/src/transactions/signing.md new file mode 100644 index 0000000000..07459a1278 --- /dev/null +++ b/docs/protocol/src/transactions/signing.md @@ -0,0 +1,151 @@ +# Transaction Signing + +In a transparent blockchain, a signer can inspect the transaction to be signed to +validate the contents are what the signer expects. However, in a shielded +blockchain, the contents of the transaction are opaque. Ideally, using a private +blockchain would enable a user to sign a transaction while also understanding +what they are signing. + +To avoid the blind signing problem, in the Penumbra protocol we allow the user +to review a description of the transaction - the `TransactionPlan` - prior to +signing. The `TransactionPlan` contains a declarative description of all details of the proposed transaction, including a plan of each action in a transparent +form, the fee specified, the chain ID, and so on. From this plan, we authorize the and build the transaction. This has the additional advantage of allowing the signer to authorize the +transaction while the computationally-intensive Zero-Knowledge +Proofs (ZKPs) are optimistically generated as part of the transaction build process. + +The signing process first takes a `TransactionPlan` and `SpendKey` and returns +the `AuthorizationData`, essentially a bundle of signatures over +the *effect hash*, which can be computed directly from the plan data. You can +read more about the details of the effect hash computation below. + +The building process takes the `TransactionPlan`, generates the proofs, and constructs a fully- +formed `Transaction`. This process is internally partitioned into three steps: +1. Each `Action` is individually built based to its specification in the `TransactionPlan`. +2. The pre-built actions to collectively used to construct a transaction with placeholder dummy signatures, that can be filled in once the +signatures from the `AuthorizationData` are ready[^1]. This intermediate state +of the transaction without the full authorizing data is referred to as the "**Unauthenticated Transaction**". +1. Slot the `AuthorizationData` to replace the placeholder signatures to assemble the final `Transaction`. + +The Penumbra protocol was designed to only require the custodian, e.g. the hardware wallet +environment, to do signing, as the generation of ZKPs can be done without access to signing keys, requiring only witness data and viewing keys. + +A figure showing how these pieces fit together is shown below: + +``` +╔════════════════════════╗ +║ Authorization ║ +║ ║ +║┌──────────────────────┐║ +║│ Spend authorization │║ +║│ key │║ ┌───────────────────┐ +║└──────────────────────┘║ │ │ +║ ║───▶│ AuthorizationData │──┐ +║ ║ │ │ │ +║┌──────────────────────┐║ └───────────────────┘ │ +║│ EffectHash │║ │ +║└──────────────────────┘║ │ +║ ║ │ +║ ║ │ +╚════════════▲═══════════╝ │ + │ │ + │ │ ┌───────────┐ + ┌───────────┴───────────┐ │ │ │ + │ │ └─────────┬────▶│Transaction│ + │ TransactionPlan │ | │ │ + │ │ │ └───────────┘ + └───────────┬───────────┘ │ + │ │ + │ │ + │ │ + ╔═══════════▼════════════╗ │ + ║ Proving ║ │ + ║ ║ │ + ║┌──────────────────────┐║ │ + ║│ WitnessData │║ │ + ║└──────────────────────┘║ ┌──────────────────────────────┐ │ + ║ ║ │ │ │ + ║ ╠──▶│ Unauthenticated Transaction ├─┘ + ║┌──────────────────────┐║ │ │ + ║│ Full viewing key │║ └──────────────────────────────┘ + ║└──────────────────────┘║ + ║ ║ + ║ ║ + ║ ║ + ╚════════════════════════╝ + +``` + +Transactions are signed used the [`decaf377-rdsa` construction](../crypto/decaf377-rdsa.md). As described briefly in that section, there are two signature domains used in Penumbra: `SpendAuth` signatures and `Binding` signatures. + +## `SpendAuth` Signatures + +`SpendAuth` signatures are included on each `Spend` and `DelegatorVote` action +(see [Multi-Asset Shielded Pool](../shielded_pool.md) and [Governance](../governance.md) +for more details on `Spend` and `DelegatorVote` actions respectively). + +The `SpendAuth` signatures are created using a randomized signing key $rsk$ and the corresponding randomized verification key $rk$ provided on the action. The purpose of the randomization is to prevent linkage of verification keys across actions. + +The `SpendAuth` signature is computed using the `decaf377-rdsa` `Sign` algorithm +where the message to be signed is the *effect hash* of the entire transaction +(described below), and the `decaf377-rdsa` domain is `SpendAuth`. + +## Effect Hash + +The effect hash is computed over the *effecting data* of the transaction, which following +the terminology used in Zcash[^2]: + +> "Effecting data" is any data within a transaction that contributes to the effects of applying the transaction to the global state (results in previously-spendable coins or notes becoming spent, creates newly-spendable coins or notes, causes the root of a commitment tree to change, etc.). + +The data that is _not_ effecting data is *authorizing data*: + +>"Authorizing data" is the rest of the data within a transaction. It does not contribute to the effects of the transaction on global state, but allows those effects to take place. This data can be changed arbitrarily without resulting in a different transaction (but the changes may alter whether the transaction is allowed to be applied or not). + +For example, the nullifier on a `Spend` is effecting data, whereas the +proofs or signatures associated with the `Spend` are authorizing data. + +In Penumbra, the effect hash of each transaction is computed using the BLAKE2b-512 +hash function. The effect hash is derived from the proto-encoding of the action - in +cases where the effecting data and authorizing data are the same, or the *body* +of the action - in cases where the effecting data and authorizing data are different. +Each proto has a unique string associated with it called its *Type URL*, +which is included in the inputs to BLAKE2b-512. +Type URLs are variable length, so a fixed-length field (8 bytes) is first included +in the hash to denote the length of the Type URL field. + +Summarizing the above, the effect hash _for each action_ is computed as: + +``` +effect_hash = BLAKE2b-512(len(type_url) || type_url || proto_encode(proto)) +``` + +where `type_url` is the bytes of the variable-length Type URL, `len(type_url)` is the length of the Type URL encoded as 8 +bytes in little-endian byte order, `proto` represents the proto used to represent +the effecting data, and `proto_encode` represents encoding the proto message as +a vector of bytes. In Rust, the Type URL is found by calling [`type_url()` on the protobuf +message](https://docs.rs/prost/latest/prost/trait.Name.html#method.type_url). + +All transaction data field effect hashes, such as the `Fee`, `MemoCiphertext`, and `TransactionParameters`, as well as the per-action effect hashes, are computed using this method. + +### Transaction Effect Hash + +To compute the effect hash of the _entire transaction_, we combine the hashes of the individual fields in the transaction body. First we include the fixed-sized effect hashes of the per-transaction data fields: the transaction parameters `eh(tx_params)`, fee `eh(fee)`, (optional) detection data `eh(detection_data)`, and (optional) memo `eh(memo)` which are derived as described above. Then, we include the number of actions $j$ and the fixed-size effect hash of each action `a_0` through `a_j`. Combining all fields: + +``` +effect_hash = BLAKE2b-512(len(type_url) || type_url || eh(tx_params) || eh(fee) || eh(memo) || eh(detection_data) || j || eh(a_0) || ... || eh(a_j)) +``` + +where the `type_url` is the variable-length Type URL of the transaction body message, and `len(type_url)` is the length of that string encoded as 8 bytes in little-endian byte order. + +## `Binding` Signature + +The `Binding` signature is computed once on the transaction level. +It is a signature over a hash of the authorizing data of that transaction, called the *auth hash*. The auth hash of each transaction is computed using the BLAKE2b-512 hash function over the proto-encoding of the _entire_ `TransactionBody`. + +The `Binding` signature is computed using the `decaf377-rdsa` `Sign` algorithm +where the message to be signed is the *auth hash* as described above, and the +`decaf377-rdsa` domain is `Binding`. The binding signing key is computed using the random blinding factors for each balance commitment. + +[^1]: At this final stage we also generate the last signature: the binding signature, which can only be added +once the rest of the transaction is ready since it is computed over the proto-encoded `TransactionBody`. + +[^2]: https://github.com/zcash/zips/issues/651