-
Notifications
You must be signed in to change notification settings - Fork 312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cardano registration metadata changes and signature specification #75
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,57 +33,88 @@ A voting key is simply an ED25519 key. How this key is created is up to the wall | |
|
||
### Associating stake with a voting key | ||
|
||
This method has been used since Fund 2. | ||
For future fund iterations, a new method making use of time-lock scripts may | ||
be introduced as described [below][future-development]. | ||
|
||
Recall: Cardano uses the UTXO model so to completely associate a wallet's balance with a voting key (i.e. including enterprise addresses), we would need to associate every payment key to a voting key individually. Although there are attempts at this (see [CIP8](../CIP-0008/CIP-0008.md)), the resulting data structure is a little excessive for on-chain metadata (which we want to keep small) | ||
|
||
Given the above, we choose to only associate staking keys with voting keys. Since most Cardano wallets only use base addresses for Shelley wallet types, in most cases this should perfectly match the user's wallet. | ||
|
||
### Registration metadata format | ||
|
||
A Catalyst registration transaction are a regular Cardano transaction with a specific transaction metadata associated with it | ||
A Catalyst registration transaction is a regular Cardano transaction with a specific transaction metadata associated with it. | ||
|
||
Notably, there should be three entries inside the metadata map: | ||
Notably, there should be four entries inside the metadata map: | ||
|
||
Voting key registration | ||
Voting registration example: | ||
``` | ||
61284: { | ||
// voting_key - CBOR byte array | ||
1: "0xa6a3c0447aeb9cc54cf6422ba32b294e5e1c3ef6d782f2acff4a70694c4d1663", | ||
// stake_pub - CBOR byte array | ||
2: "0xad4b948699193634a39dd56f779a2951a24779ad52aa7916f6912b8ec4702cee", | ||
// address - CBOR byte array | ||
3: "0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee47b60edc7772855324c85033c638364214cbfc6627889f81c4" | ||
// reward_address - CBOR byte array | ||
3: "0x00588e8e1d18cba576a4d35758069fe94e53f638b6faf7c07b8abd2bc5c5cdee47b60edc7772855324c85033c638364214cbfc6627889f81c4", | ||
// slot_number | ||
4: 5479467 | ||
} | ||
``` | ||
|
||
Signing the voting public key with the staking private key | ||
The entries under keys 1, 2, 3, and 4 represent the Catalyst voting key, | ||
the staking key on the Cardano network, the address to receive rewards, | ||
and the current slot number, respectively. A registration with these metadata | ||
will be considered valid if the following conditions hold: | ||
|
||
- The slot number of the block including this transaction is equal to | ||
the slot number value under key 4 or its increment by one. | ||
- The reward address is a Shelley address discriminated for the same network | ||
this transaction is submitted to. | ||
|
||
To produce the signature field, the CBOR representation of a map containing | ||
a single entry with key 61284 and the registration metadata map in the | ||
format above is formed, designated here as `sign_data`. | ||
This data is signed with the staking key using one of the two methods, | ||
discriminated by the key number in the signature metadata map: | ||
|
||
1: `sign_data` is signed as is using Ed25519. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is no longer supported in fund4, it has to be hashed first. |
||
|
||
2: The blake2b-256 hash of `sign_data` is signed using Ed25519. | ||
|
||
Signature example: | ||
``` | ||
61285: { | ||
// signature - ED25119 signature CBOR byte array | ||
1: "0x8b508822ac89bacb1f9c3a3ef0dc62fd72a0bd3849e2381b17272b68a8f52ea8240dcc855f2264db29a8512bfcd522ab69b982cb011e5f43d0154e72f505f007" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't the key be updated to "2"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The example is still valid, applications should be free to use scheme 1 if they prefer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, it should use 1. 2 will be ignored by the snapshot tools. We will not support both signature options. |
||
} | ||
``` | ||
|
||
This corresponds to the following CDDL definition | ||
### Metadata schema | ||
|
||
The format above can be described by the following CDDL definition: | ||
|
||
``` | ||
registration_cbor = { | ||
61284: key_registration | ||
, 61285: registration_signature | ||
61284: key_registration, | ||
61285: registration_signature | ||
} | ||
|
||
$voting_pub_key /= bytes .size 32 | ||
$staking_pub_key /= bytes .size 32 | ||
$ed25519_signature /= bytes .size 64 | ||
$address /= bytes | ||
$reward_address /= bytes | ||
$slot_number /= uint | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this does not have to be a slot number, it can be any int, but slot makes it easy to not have to understand previous state to update a registration. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see two potential issues with the slot number being an int without specific constraints:
If not enforced by the snapshotting tool, I would at least put in the CIP that standard wallet implementations shall choose as the nonce the current slot number to ensure users of standard wallets don't face issues re-registering their voting keys should they migrate to another wallet |
||
|
||
key_registration = { | ||
1 : $voting_pub_key | ||
, 2 : $staking_pub_key | ||
, 3 : $address | ||
1 : $voting_pub_key, | ||
2 : $staking_pub_key, | ||
3 : $reward_address, | ||
4 : $slot_number, | ||
} | ||
|
||
registration_signature = { | ||
1 : $ed25519_signature | ||
1 : $ed25519_signature // | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here should probably be a comment that this field is not used anymore as the signature scheme changed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea is that either of the schemes can be used interchangeably. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then I think the key should be prefixed with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, do we want to allow both keys at the same time? I can see that leading to hard-to-predict behavior/implementation missalignment across different parts of the system for the case when one signature would be valid and the other not, so I'd advocate for enfocing a strict "either key 1 is present or key 2, but not both" |
||
2 : $ed25519_signature | ||
} | ||
``` | ||
|
||
|
@@ -93,17 +124,30 @@ Here an example using CBOR diagnostic notation | |
{ | ||
61284: { | ||
1: h'8253C95609BC62C0443276FE2A1872B87CB11C06185FFDBB56C7CE8352EEF2A3', | ||
2: h'345080C6DDFF7154B4ED4A622558AA0EAABD8CE7E2701C92B6858EA76DCECBCE' | ||
2: h'AD4B948699193634A39DD56F779A2951A24779AD52AA7916F6912B8EC4702CEE', | ||
3: h'00588E8E1D18CBA576A4D35758069FE94E53F638B6FAF7C07B8ABD2BC5C5CDEE47B60EDC7772855324C85033C638364214CBFC6627889F81C4', | ||
4: 5479467 | ||
}, | ||
61285: { | ||
1: h'7D88F34D778B7A4C76AA53FF5D9506DC5B92D25575B43AF75D66DC05082A2BCFF44FCEDDAB15DBA0C23C56A09A15367A9803E24A388AAFB8498EF72190407B0D' | ||
} | ||
} | ||
``` | ||
|
||
### Future development | ||
|
||
[future-development]: #future-development | ||
|
||
A future change of the Catalyst system may make use of a time-lock script to commit ADA on the mainnet for the duration of a voting period. The voter registration metadata in this method will not need an association | ||
with the staking key. Therefore, the `staking_pub_key` map entry | ||
and the `registration_signature` payload with key 61285 will no longer | ||
be required. | ||
|
||
## Changelog | ||
|
||
Fund 3 added the `address` inside the `key_registration` field. | ||
Fund 3 added the `reward_address` inside the `key_registration` field. | ||
|
||
Fund 4 added the `slot_number` field to prevent replay attacks. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd call this nonce rather than slot number |
||
|
||
It was planned that since Fund 4, `registration_signature` and the `staking_pub_key` entry inside the `key_registration` field will be deprecated. | ||
This has been deferred to a future revision of the protocol. | ||
|
||
## Copyright | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How could you possibly guarantee this? Slots in Cardano are only 2 seconds and not all of them have a block in it.
On top of that, it would allow any pool to censor Catalyst registration transactions by purposely skipping them rendering them invalid for future slots.
Additionally, signing a transaction for hardware wallets requires user steps, so by the time the user is done with all the steps the tx may very well no longer be valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, the user may need even several minutes to sign the transaction on a hardware wallet (which needs to know the slot number in advance, making it de-facto impossible to sign a valid voting key registration). Could you @mzabaluev explain the rationale behind this constraint?
I gather that the motivation behind this constraint is likely preventing the users from submitting a key registration with too big of a value of the counter which would effectively make their key un-updatable - for that purpose a constraint that the slot number should be no higher than the slot number of the block in which the registration is submitted should do.
Another motivation could be avoiding state storage - Not sure how the current "active voting keys" are stored but they probably need to be stored somehow and storing a counter alongside them (to be able to compare it against subsequent re-registrations) seems more viable than forcing such strict consistency with the block number where the key registration would be submitted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the two slot time window is too short, so either a larger validity timeout is needed or a different mechanism to provide a unique nonce.
What number of slots would be sufficient to prevent such censorship, assuming an honest majority among the pools?
This time window defines how far apart successive re-registrations need to be for the same staking key, to make it impossible to nullify the newer registration by replaying the older.
It's rather so that old registrations cannot be replayed, reverting a possible newer registration of a different voting key (or a revocation, whenever that use case is supported) with the same staking key. But it could also prevent the problem you describe, if we apply the "larger slot number wins" rule to resolving conflicting registrations rather than the order of transactions (which may be problematic if the transactions happen to be in the same block and there is no UTxO-imposed order between them).
It's certainly doable for the validation logic on the snapshotting tool, but I'm not sure about all wallets.
A wallet would be expected to recover the last successful registration transaction for the selected public staking key to obtain the value of the counter. If this is compatible with constraints posed by HW wallets, we can change the specification to use this stateful scheme.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why would a wallet need to recover the counter from a previous registration - if the counter is chosen as the current slot number, that's a global increasing value. If we are afraid about the counter getting bigger than it should be, Catalyst can just ignore registrations with a value of the counter higher than the slot where they have been submitted. However, if a user somehow signs by mistake a registration with a future slot number, somebody may abuse that to overwrite user's registration later on, but that's a problem that neither the currently proposed design addresses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slot number is only a suggestion of what to use for the nonce. It's nice because it always increases, but fundamentally, it can be any number that never goes backwards. e.g. 1, 2, 3, 4, 3 would result in 4 being the one used and ignore the last. Note that this nonce is not implemented for fund4 (it's included, but snapshot ignores it). We want to get the schema correct so we can properly scope out adding this for fund 5, but also want to stabilize so teams aren't having to continuously make changes.