|
| 1 | +# ADR-003 - Service Registration |
| 2 | + |
| 3 | +Status: In development |
| 4 | +Authors: @distractedm1nd |
| 5 | + |
| 6 | +## Rationale |
| 7 | + |
| 8 | +[ADR-002](./adr-002-create-account-op.md) discussed basic ideas surrounding account creation and the concept of external account sources. |
| 9 | + |
| 10 | +To quote ADR-002, |
| 11 | + |
| 12 | +> Once a new user is added to the JMT, we know that all updates to a hashchain are valid by construction- updates must be signed by an already existing key in the hashchain. But who gets to add the first key for a user? Why should we trust that the first key added actually comes from the user? |
| 13 | +> |
| 14 | +
|
| 15 | +While ADR-002 discussed the concept behind a `CreateAccount` operation, it proposed that all account services must be enshrined to the protocol. This ADR discusses permissionless service (a.k.a `AccountSource`) registration and potential resource pricing for account creation. |
| 16 | + |
| 17 | +## Proposal |
| 18 | + |
| 19 | +Until we are confident in the state growth benchmarks, and to maintain simplicity, we will start with whitelisted service registration. The effects of this are following: |
| 20 | + |
| 21 | +1. Registering a service requires a signature from the prover/sequencer |
| 22 | +2. The only way to add nodes to the tree is by completing a service-specific challenge (either a signature check or providing a groth16 proof) |
| 23 | + |
| 24 | +This may look something like this: |
| 25 | + |
| 26 | +```rust |
| 27 | +/// Represents a public key supported by the system. |
| 28 | +pub enum PublicKey { |
| 29 | + Secp256k1(Vec<u8>), // Bitcoin, Ethereum |
| 30 | + Ed25519(Vec<u8>), // Cosmos, OpenSSH, GnuPG |
| 31 | + Curve25519(Vec<u8>), // Signal, Tor |
| 32 | +} |
| 33 | + |
| 34 | +/// Represents a signature bundle, which includes the index of the key |
| 35 | +/// in the user's hashchain and the associated signature. |
| 36 | +pub struct SignatureBundle { |
| 37 | + key_idx: u64, // Index of the key in the hashchain |
| 38 | + signature: Vec<u8>, // The actual signature |
| 39 | +} |
| 40 | + |
| 41 | +/// A service-specific challenge required to create an account or perform other actions. |
| 42 | +pub enum ServiceChallenge { |
| 43 | + Groth(groth16::VerifyingKey), |
| 44 | + Signed(PublicKey), |
| 45 | +} |
| 46 | + |
| 47 | +/// Input required to complete a challenge for account creation. |
| 48 | +pub enum ServiceChallengeInput { |
| 49 | + Groth(groth16::Proof), |
| 50 | + Signed(Vec<u8>), // Signature bytes |
| 51 | +} |
| 52 | + |
| 53 | +/// Operations that can be performed on accounts or services. |
| 54 | +pub enum Operation { |
| 55 | + CreateAccount(CreateAccountArgs), |
| 56 | + RegisterService(RegisterServiceArgs), |
| 57 | + AddKey(KeyOperationArgs), |
| 58 | + RevokeKey(KeyOperationArgs), |
| 59 | + AddNote(NoteOperationArgs), |
| 60 | +} |
| 61 | + |
| 62 | +/// Arguments for creating an account with a service. |
| 63 | +pub struct CreateAccountArgs { |
| 64 | + id: String, // Account ID |
| 65 | + value: String, // Initial value |
| 66 | + service_id: String, // Associated service ID |
| 67 | + challenge: ServiceChallengeInput, // Challenge input for verification |
| 68 | +} |
| 69 | + |
| 70 | +/// Arguments for registering a new service. |
| 71 | +pub struct RegisterServiceArgs { |
| 72 | + id: String, // Service ID |
| 73 | + gate: ServiceChallenge, // Challenge gate for access control |
| 74 | + signature: Option<Vec<u8>> // Signature from sequencer allowing service registration |
| 75 | +} |
| 76 | + |
| 77 | +/// Common structure for operations involving keys (adding or revoking). |
| 78 | +pub struct KeyOperationArgs { |
| 79 | + id: String, // Account ID |
| 80 | + value: PublicKey, // Public key being added or revoked |
| 81 | + signature: SignatureBundle, // Signature to authorize the action |
| 82 | +} |
| 83 | + |
| 84 | +/// Arguments for adding a note (plaintext) to an account. |
| 85 | +pub struct NoteOperationArgs { |
| 86 | + id: String, // Account ID |
| 87 | + value: String, // Plaintext note |
| 88 | + signature: SignatureBundle, // Signature to authorize the action |
| 89 | +} |
| 90 | + |
| 91 | +``` |
| 92 | + |
| 93 | +This differs from the existing implementation significantly, whose only account source is a SignedBySequencer. Futher adjustments were made to AddKey and RevokeKey to enable hashchain verification using a SignatureBundle as originally intended. |
| 94 | + |
| 95 | +## Effects |
| 96 | + |
| 97 | +If we determine state growth will be an issue, we can add resource pricing by requiring service registration to take a Celestia TxHash of a deposit to the sequencer, which provides a fixed amount of credits that can be spent to make accounts. |
| 98 | + |
| 99 | +The reasoning behind not taking this approach from the get-go is that it requires us zk-proving inclusion of the TxHash from a Celestia block’s reserved namespace via the NMT. While this is feasible, especially with SP1, we don’t want to overengineer it until we see the benchmarks. |
| 100 | + |
| 101 | +The ideal case is that state growth is not enough of an issue with the NMT that the price of TIA backing a CreateAccount operation is enough to deter spam. This is also assuming we require non-enshrined account source creation to submit their operations directly to the DA layer, avoiding the subsidized operations the sequencer provides. |
| 102 | + |
| 103 | +## Further Considerations |
| 104 | + |
| 105 | +A further consideration is the usage of the above operation types for using the Prism state tree as a nullifier tree, enabling a native shielded pool. Unfortunately, I have determined that it is not possible without running an extra protocol on top. First consider a shielded pool service could be registered whose groth16 verifying key corresponds to a circuit that verifies the user knows the plaintext to an encrypted note in their hashchain, whose hash does not yet exist as an account in the JMT. The user would submit a CreateAccount operation which would actually be meant to spend a nullifier for one of their notes. But this restricts the consumption and output to a single note, and it does not account for intermediate state roots. |
| 106 | + |
| 107 | +Enabling a native shielded pool will likely require a few more operation types which handle these special cases, which opens a huge pandoras box about what asset to use, if it would be native to Prism, and if not, how bridging would work. |
| 108 | + |
| 109 | +## Action Items |
| 110 | + |
| 111 | +The team will implement a permissioned yet flexible service registration system, that can be made permissionless by removing the signature verification requirement once key benchmarks are investigated and discussed. |
| 112 | + |
| 113 | +The team will discuss extending the protocol to enable a native shielded pool in a future ADR before the first version of the protocol is finalized. |
0 commit comments