ChainTransport::with_peer_reads(db) is never called — the NC-12 corroborated chain path fails closed in production
Found by the dig-node#574 lane while investigating whether bond confirmation could move off the third-party oracle. Reported rather than fixed, correctly — it is pre-existing and wider than that ticket.
What is true today
ChainTransport::corroborated_chain_source exists, and CorroboratedChainSource implements the NC-12 shape properly: agreement across independently-queried peers, refusing rather than falling back. But with_peer_reads(db) is never called anywhere in dig-node-service, so the peer-read half is never populated and the corroborated source fails closed in production.
The consequence is visible on a live host. Every chain read logs:
dig_wallet::sage::rpc: wallet balance read routed to the third-party chain oracle {"tier":"fallback"}
dig_wallet::sage::rpc: coin-spend read routed to the third-party chain oracle {"tier":"fallback"}
And the balance surface says so itself: NOT CURRENT — this node cannot say what height this answer reflects; it is not evidence that there is nothing there.
This is not new and not caused by #574 — it affects the pre-existing observe_chain and coin_confirmation calls identically. #574 simply made it visible by asking whether its new re-verification could use the corroborated path.
Why it matters more than "a slower read"
The mirror-coin path writes to chain and locks $DIG collateral. Today the node decides what its own bonds are by asking a single third party, with no corroboration and no height it can vouch for. NC-12's whole posture — believe a fact because independent sources agree, never because one source asserted it — is implemented and then not switched on.
Measured tonight, the practical cost: an independent api.coinset.org query showed two funding coins already spent at block 9251995 while dign's own read still reported them unspent. The node self-corrected a pass later, so nothing was lost — but it was, for a window, wrong about its own money on the word of one source.
Why it is a small change with a large effect — and what to establish first
coin_record(coin_id) is already corroborated under quorum via #503. What is missing is only that nothing populates the peer-read side.
So establish, before writing anything:
- Why is
with_peer_reads never called? It may be deliberate — an unfinished rollout, a performance concern, or a dependency that is not available at that construction site. A deliberate omission with a reason is a different ticket from an oversight, and the reason may be good.
- What
db does it need, and is that handle available where ChainTransport is built? If it is not, the fix is a plumbing change and larger than it looks.
- What happens when too few peers answer?
CorroboratedChainSource refuses rather than falling back — which is correct, but it means enabling this could turn "a slow, uncertain answer" into "no answer" on a node with poor peer connectivity. That trade must be deliberate, and there needs to be an honest surface for it rather than a silent stall.
The failure direction to preserve
Refusing is the right behaviour and must not be softened into a fallback. A corroborated source that quietly reverts to the oracle when peers are short would be strictly worse than today: it would carry the appearance of corroboration with none of the property. If the answer is "some nodes cannot corroborate", say so on the surface — do not paper it over.
Related
ChainTransport::with_peer_reads(db)is never called — the NC-12 corroborated chain path fails closed in productionFound by the dig-node#574 lane while investigating whether bond confirmation could move off the third-party oracle. Reported rather than fixed, correctly — it is pre-existing and wider than that ticket.
What is true today
ChainTransport::corroborated_chain_sourceexists, andCorroboratedChainSourceimplements the NC-12 shape properly: agreement across independently-queried peers, refusing rather than falling back. Butwith_peer_reads(db)is never called anywhere indig-node-service, so the peer-read half is never populated and the corroborated source fails closed in production.The consequence is visible on a live host. Every chain read logs:
And the balance surface says so itself:
NOT CURRENT — this node cannot say what height this answer reflects; it is not evidence that there is nothing there.This is not new and not caused by #574 — it affects the pre-existing
observe_chainandcoin_confirmationcalls identically. #574 simply made it visible by asking whether its new re-verification could use the corroborated path.Why it matters more than "a slower read"
The mirror-coin path writes to chain and locks $DIG collateral. Today the node decides what its own bonds are by asking a single third party, with no corroboration and no height it can vouch for. NC-12's whole posture — believe a fact because independent sources agree, never because one source asserted it — is implemented and then not switched on.
Measured tonight, the practical cost: an independent
api.coinset.orgquery showed two funding coins already spent at block 9251995 while dign's own read still reported them unspent. The node self-corrected a pass later, so nothing was lost — but it was, for a window, wrong about its own money on the word of one source.Why it is a small change with a large effect — and what to establish first
coin_record(coin_id)is already corroborated under quorum via #503. What is missing is only that nothing populates the peer-read side.So establish, before writing anything:
with_peer_readsnever called? It may be deliberate — an unfinished rollout, a performance concern, or a dependency that is not available at that construction site. A deliberate omission with a reason is a different ticket from an oversight, and the reason may be good.dbdoes it need, and is that handle available whereChainTransportis built? If it is not, the fix is a plumbing change and larger than it looks.CorroboratedChainSourcerefuses rather than falling back — which is correct, but it means enabling this could turn "a slow, uncertain answer" into "no answer" on a node with poor peer connectivity. That trade must be deliberate, and there needs to be an honest surface for it rather than a silent stall.The failure direction to preserve
Refusing is the right behaviour and must not be softened into a fallback. A corroborated source that quietly reverts to the oracle when peers are short would be strictly worse than today: it would carry the appearance of corroboration with none of the property. If the answer is "some nodes cannot corroborate", say so on the surface — do not paper it over.
Related