cardano-foundation / cardano-foundation/cardano-rosetta-java
1: `/construction/derive` — CIP-113 Smart Wallet Address Derivation
- Dominant language
- Java
- Stars
- 26
- Forks
- 15
- Avg merge
- 5d 3h
- Merged PRs (30d)
- 2
Description
### Background
The `/construction/derive` endpoint derives a Cardano account address from a public key. Cardano Rosetta will add a Cardano-specific `"CIP-113"` address type for the exchange enterprise-deposit convention.
It derives a CIP-113 smart-wallet base address whose payment credential is the configured `programmableLogicBase` (PLB) script hash and whose stake credential is the Blake2b-224 hash of the supplied enterprise payment public key.
This allows an exchange to derive the exchange-controlled CIP-113 smart-wallet deposit address corresponding to an existing enterprise deposit public key.
Phase 1 supports one explicitly configured PLB script hash per Rosetta network instance. It does not query the CIP-113 registry, identify whether a policy is programmable, discover deployments, or validate CIP-113 transaction semantics.
### Derivation
```text
Input:
public_key.hex_bytes = exchange enterprise payment public key
public_key.curve_type = edwards25519
metadata.address_type = CIP-113
publicKeyBytes = hexDecode(public_key.hex_bytes)
userCredential = Blake2b-224(publicKeyBytes)
SmartWallet = BaseAddress(
payment = ScriptCredential(configuredPLBScriptHash),
stake = KeyCredential(userCredential),
network = request.network_identifier
)
```
Credential sizes:
```text
Ed25519 public key: 32 bytes = 64 hexadecimal characters
User key hash: 28 bytes = 56 hexadecimal characters
PLB script hash: 28 bytes = 56 hexadecimal characters
```
The PLB hash is used directly as a script credential. It must not be padded, represented as a public key, or hashed again. A separate `metadata.staking_credential` is neither needed nor accepted.
### Request and response example
For this example, the server is configured with PLB script hash:
```text
f2182b00a37bd746e20575c9af01ab31312213514cd31e872e0a2a3e
```
Request:
```json
{
"network_identifier": {
"blockchain": "cardano",
"network": "preprod"
},
"public_key": {
"hex_bytes": "1b400d60aaf34eaf6dcbab9bba46001a23497886cf11066f7846933d30e5ad3f",
"curve_type": "edwards25519"
},
"metadata": {
"address_type": "CIP-113"
}
}
```
Response:
```json
{
"account_identifier": {
"address": "addr_test1zreps2cq5daaw3hzq46untcp4vcnzgsn29xdx8589c9z504mgrc6v3au3rqm66mn3kuwke340kfxga82tl7kh2nke8as48q6r3"
}
}
```
### Error behavior
Providing `metadata.staking_credential` with `address_type: "CIP-113"` must be rejected rather than ignored. If a new dedicated error is retained:
```json
{
"code": 4060,
"message": "Staking credential not allowed with CIP-113 address type",
"retriable": false,
"details": {
"message": "For the exchange enterprise-address convention, CIP-113 derives the user credential from the top-level public_key and uses the configured PLB script hash as the payment credential. Do not provide metadata.staking_credential."
}
}
```
The new error must be registered centrally and advertised by `/network/options`. Reusing an existing suitable invalid-metadata error is also acceptable if the issue and tests use it consistently.
`address_type` remains case-sensitive. `"cip113"` returns the existing `INVALID_ADDRESS_TYPE` error rather than introducing a duplicate error code:
```json
{
"code": 4016,
"message": "Provided address type is invalid",
"retriable": false
}
```
### PLB configuration
Define and validate a dedicated property. Suggested name:
```yaml
cardano:
rosetta:
cip113:
programmable-logic-base-script-hash: "f2182b00a37bd746e20575c9af01ab31312213514cd31e872e0a2a3e"
```
The final name should follow repository configuration conventions.
- Decode the value as hexadecimal.
- Require exactly 28 decoded bytes.
- Reject empty, non-hexadecimal, short, and long values.
- Use it directly as a script hash; do not hash it again.
- Use one configured PLB per Rosetta network instance for Phase 1.
- Do not silently substitute a default deployment.
- If CIP-113 is enabled, fail startup for missing or malformed configuration. If support is optional, fail the CIP-113 request with a documented configuration error.
### Implementation approach
Add a dedicated derivation method because ordinary base derivation hashes two public keys, while CIP-113 combines a configured script hash with a key-derived credential:
```java
String getCip113Address(PublicKey userPublicKey, NetworkEnum network);
```
Reuse the Cardano Client Library through `CardanoAddressUtils` for the address header, network tag, and Bech32 encoding:
```java
public String getCip113Address(PublicKey userPublicKey, NetworkEnum network) {
byte[] userKeyHash = getHdPublicKeyFromRosettaKey(userPublicKey).getKeyHash();
byte[] plbScriptHash = cip113Config.getPlbScriptHashBytes();
return CardanoAddressUtils.getAddress(
plbScriptHash,
userKeyHash,
(byte) 0x10,
network.getNetwork(),
com.bloxbean.cardano.client.address.AddressType.Base)
.getAddress();
}
```
Prefer a named constant for the script-payment/key-stake address kind instead of an unexplained `0x10` literal.
### Acceptance criteria
- [ ] `POST /construction/derive` with `address_type: "CIP-113"` derives an exchange-controlled smart-wallet address from the supplied enterprise payment public key.
- [ ] The supplied public-key bytes are hashed with Blake2b-224.
- [ ] The result is a Cardano base address with a script payment credential and key stake credential.
- [ ] The payment credential equals the configured 28-byte PLB script hash exactly.
- [ ] The PLB script hash is not padded, represented as a public key, or hashed again.
- [ ] The stake credential equals Blake2b-224 of the decoded supplied public-key bytes.
- [ ] The returned address uses the network selected by `network_identifier`.
- [ ] `metadata.staking_credential` is rejected rather than ignored and returns the selected documented error.
- [ ] Wrong-case `address_type: "cip113"` returns existing error `4016`.
- [ ] Missing or malformed PLB configuration follows the documented behavior.
- [ ] No registry lookup, deployment discovery, or CIP-113 policy validation occurs.
- [ ] Existing `Enterprise`, `Base`, and `Reward` behavior remains unchanged.
### Test cases
#### `Cip113DeriveTest`
- The fixed public-key/PLB vector returns the expected address shown above.
- Decode the address and independently verify its address type, network, payment credential, and stake credential.
- Two different public keys produce different smart-wallet addresses.
- Identical public key, PLB, and network inputs produce the same address.
- Changing the configured PLB changes the payment credential and address.
- Mainnet returns an `addr1...` address with the mainnet network tag.
- Preview/Preprod returns an `addr_test1...` address with the testnet network tag.
#### `Cip113DeriveValidationTest`
- Missing public key follows existing error behavior.
- Invalid hexadecimal public-key bytes are rejected.
- Unsupported public-key lengths are rejected.
- Unsupported curve types are rejected; `edwards25519` is accepted.
- Existing supported public-key forms remain consistent with Enterprise derivation.
- A supplied `metadata.staking_credential` is rejected with the selected documented error.
- Missing, empty, non-hexadecimal, short, and long PLB configuration is rejected according to the chosen startup/request behavior.
- Wrong-case `"cip113"` returns existing error `4016`.
#### Regression tests
- Existing `Enterprise` derivation is unchanged.
- Existing `Base` derivation is unchanged.
- Existing `Reward` derivation is unchanged.
- Omitting `metadata.address_type` continues to select the existing default.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the /construction/derive entry point and compare the existing Enterprise, Base, and Reward derivation behavior with the proposed getCip113Address method and CardanoAddressUtils usage. Read the Cip113DeriveTest and Cip113DeriveValidationTest requirements first, then verify the configured PLB, derived credentials, network encoding, validation errors, and regression behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, blockchain
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100