Repository navigation
Description
Hi team! Thank you for all your work to get this library ready for the HF! The feat/ledger-cbor branch (#748) already covers a lot of Dijkstra. However, I ran a few tests on a devnet with cardano-node 11.2.0, against the releases and that branch, and found several things to work on before it's ready. Since this project is (obviously) critical for the DApp ecosystem, please let me know if we (I, my team at AlwaysTrue, or anyone at IOG) can help with this!:
- Use the deposit-carrying certificates in QuickTx
registerStakeAddressandderegisterStakeAddress - Keep the Dijkstra fields when signing a
Transactionobject (feat(ledger-cbor): one era-aware ledger CBOR reader (ADR 0003, M0-M6) #748) - Keep the transaction id of a key-hash body key 14 read in Dijkstra (feat(ledger-cbor): one era-aware ledger CBOR reader (ADR 0003, M0-M6) #748)
- Don't drop Dijkstra fields silently when reading without an era (feat(ledger-cbor): one era-aware ledger CBOR reader (ADR 0003, M0-M6) #748)
- Support PlutusV4 and the new body fields in QuickTx, with V4 cost models from the backends (Support Plutus V4 scripts (Dijkstra era / Leios) #707)
- Read the reference-script fee multiplier and stride from the protocol parameters
- Backport the duplicate-witness fix to 0.7.x
- Merge feat(ledger-cbor): one era-aware ledger CBOR reader (ADR 0003, M0-M6) #748 and release it
Testing scenario
Tested on 2026-11-11: 0.7.3 and 0.8.0-pre7 from Maven Central, and the feat/ledger-cbor branch at d3324ec3 built with JDK 21, against cardano-node 11.2.0 on a cardano-testnet devnet (magic 42) that hard-forked from Conway to Dijkstra and was raised to protocol version 12. The library built and signed every transaction, which was then submitted as raw CBOR through cardano-submit-api 11.2.0. UTxOs and protocol parameters came from cardano-cli, so the Blockfrost, Koios, and Ogmios backends were not tested against Dijkstra. Also not tested: required top-level guards (key 24), starting balance intervals (key 27), pool registration with a BLS key, and parameter-change proposals.
What already works:
-
0.7.3 and 0.8.0-pre7: payments, metadata, and a PlutusV3 lock and spend are accepted. So are
RegCertandUnregCertswapped in throughpreBalanceTx. -
The branch: models written with
serialize(Era.Dijkstra)and signed over the bytes are accepted for each of these cases:- a three-element payment
- a direct deposit (the account balance rose by the deposit)
- a guard
- an account balance interval
- a PlutusV4 reference script
- a PlutusV4 lock and spend
- a top-level transaction with one sub-transaction
The node rejects a guard on a key that didn't sign and an interval the balance falls outside, as it should.
-
Reading on the branch: the 25 transactions of an earlier devnet run, read with
deserialize(bytes, Era.Dijkstra), all give the on-chain id throughRawTx. -
The branch's tests:
common-spec,ledger-cbor,transaction-spec, andplutusrun 1,100 tests with 0 failures, without the external CBOR dataset.
To reproduce:
The devnet scripts are in scripts/readiness/devnet. Its README builds the devnet, and jvm-probe/ccl-e2e has the QuickTx harness (E2E.java) and the branch probe (BranchProbe.java). Logs and the CBOR of every submitted transaction: 2026-11-11-ccl.
Issues
Use the deposit certificates in QuickTx stake registration
registerStakeAddress and deregisterStakeAddress add StakeRegistration and StakeDeregistration (certificate types 0 and 1), which Dijkstra removes (StakeRegistrationIntent.java#L120, StakeDeregistrationIntent.java#L179). This is the same on master and the branch. With 0.7.3 and 0.8.0-pre7, the node rejects both transactions:
EraMismatch {ledgerEraName = "Dijkstra", otherEraName = "Conway"}
The same transactions with RegCert and UnregCert instead (plus the stake key witness that RegCert needs) are accepted. These certificates are also valid in Conway. Tools that register stake keys through QuickTx, such as yaci-devkit's cluster setup, hit this too.
Keep the Dijkstra fields when signing a Transaction object
On the branch, TransactionSigner.sign(Transaction, …) serializes in the transaction's era and signs, then re-reads the result with era-less Transaction.deserialize(signedTxBytes) (TransactionSigner.java#L32-L59). That reads the bytes as Conway, so body keys 23-27 are lost. A direct deposit signed this way loses key 25 and is rejected:
ValueNotConservedUTxO … InvalidWitnessesUTXOW
The same transaction signed with sign(byte[], …) is accepted. Account.sign(tx) and the QuickTx signers use the Transaction path.
Keep the id of a key-hash key 14
A Conway body with required_signers (key hashes in key 14) that is accepted after the fork stays on chain in that form. The branch reads key 14 as guards and writes it back as credentials. getTxHash() on the model then differs from the on-chain id, and re-signing or re-submitting that model changes its body. RawTx gives the right id.
Era-less reads drop Dijkstra fields silently
Transaction.deserialize(bytes) still reads as Conway on the branch. On a direct deposit or a sub-transaction, it drops the field without an error and computes a different id, as 0.8.0-pre7 does today. Callers that don't pass an era will get these wrong: the backends' transaction parsing, the evaluators, Account.sign(String), and downstream codecs.
PlutusV4 and the new fields in QuickTx and the backends
The branch's ADR 0004 leaves QuickTx and the builders (W-M2) for later. Until then, QuickTx can't build any of the following:
- a three-element transaction
- a PlutusV4 script
- a guard, sub-transaction, direct deposit, or balance interval
Fee and balance calculation also don't account for direct deposits or sub-transaction execution units. The backends map cost models for V1-V3 only (Ogmios ProtocolParametersDto.java#L81-L83, Koios KoiosEpochService.java#L251-L278), so a V4 cost model never reaches the builder. #707 tracks the PlutusV4 part.
Reference-script fee parameters
The reference-script fee uses a fixed multiplier of 1.2 and a fixed stride of 25,600 bytes (FeeCalculationServiceImpl.java#L229-L230). Dijkstra makes them protocol parameters 36 (stride) and 37 (multiplier). The devnet's values equal the constants, so fees are right today, but a parameter change would make the fee too low.
Backport the duplicate-witness fix to 0.7.x
Dijkstra rejects witness sets with duplicates. In 0.7.3, adding the same key twice with withSigner writes two identical vkey witnesses, and the node rejects the transaction. 0.8 already removes the duplicate (bd9e1c82), but release/0.7.x doesn't have it. Unrelated to Dijkstra, 0.7.3 also computes the script data hash wrongly when two spent outputs share a datum hash, so the node rejects it with ScriptIntegrityHashMismatch, on Conway too. The fix is in 0.8 (a39f5924, #603).
Merge #748 and release it
No release has the Dijkstra models yet; the newest is 0.8.0-pre7. Yaci, Yaci Store, and Yano follow CCL's release (ADR 0003), so they can't ship Dijkstra block reading before it either.
Activity
satran004 commentedon Nov 13, 2026
Thanks, @rober-m. Yes, we have started with CBOR parsing for the Dijkstra era, as this will also be used by other projects like Yaci, Yano and Yaci Store.
Thanks for the list of gaps and issues. I think it will help us move faster. We'll look into them soon and update the progress here.
The goal is to get the CBOR encoding/decoding layer ready first and publish a release so that it can be used by downstream projects like Yaci, instead of maintaining a separate parsing layer there, as we do today.
After that, we'll start looking into higher level transaction builder like QuickTx.