Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.

Commit 60912bc

Browse files
Attempt to produce SPV proofs in the valid history window (#2280)
When an SPV proof would have a root outside of the valid history window, i.e. the proof would be expired, attempt to produce a proof which has a root inside the valid history window instead if possible. Co-authored-by: Lars Kuhtz <lars@kadena.io>
1 parent 155b77d commit 60912bc

File tree

2 files changed

+58
-15
lines changed

2 files changed

+58
-15
lines changed

src/Chainweb/SPV/CreateProof.hs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import Chainweb.SPV
5656
import Chainweb.TreeDB
5757
import Chainweb.Utils
5858
import Chainweb.Version
59+
import Chainweb.Version.Guards
5960
import Chainweb.WebBlockHeaderDB
6061

6162
import Chainweb.Storage.Table
@@ -109,7 +110,7 @@ createTransactionProof_
109110
-- ^ The index of the transaction in the block
110111
-> IO (TransactionProof SHA512t_256)
111112
createTransactionProof_ headerDb payloadDb tcid scid bh i = do
112-
trgHeader <- minimumTrgHeader headerDb tcid scid bh
113+
trgHeader <- findTrgHeader headerDb tcid scid bh
113114
TransactionProof tcid
114115
<$> createPayloadProof_ transactionProofPrefix headerDb payloadDb tcid scid bh i trgHeader
115116

@@ -215,7 +216,7 @@ createTransactionOutputProof_
215216
-- ^ The index of the transaction in the block
216217
-> IO (TransactionOutputProof SHA512t_256)
217218
createTransactionOutputProof_ headerDb payloadDb tcid scid bh i = do
218-
trgHeader <- minimumTrgHeader headerDb tcid scid bh
219+
trgHeader <- findTrgHeader headerDb tcid scid bh
219220
TransactionOutputProof tcid
220221
<$> createPayloadProof_ outputProofPrefix headerDb payloadDb tcid scid bh i trgHeader
221222

@@ -480,7 +481,11 @@ crumbsToChain db srcCid trgHeader
480481
let !adjIdx = fromJuste $ blockHashRecordChainIdx (view blockAdjacentHashes cur) h
481482
go adjpHdr t ((adjIdx, cur) : acc)
482483

483-
minimumTrgHeader
484+
-- | The minimum number of blocks before a newly created proof root expires.
485+
proofSlack :: BlockHeight
486+
proofSlack = 100
487+
488+
findTrgHeader
484489
:: WebBlockHeaderDb
485490
-> ChainId
486491
-- ^ target chain. The proof asserts that the subject is included in
@@ -491,8 +496,26 @@ minimumTrgHeader
491496
-- ^ The block height of the transaction, i.e. the subject on the source
492497
-- chain.
493498
-> IO BlockHeader
494-
minimumTrgHeader headerDb tcid scid bh = do
499+
findTrgHeader headerDb tcid scid bh = do
495500
trgHeadHeader <- maxEntry trgChain
501+
let trgHeadHeight = trgHeadHeader ^. blockHeight
502+
let trgHeight = int $
503+
case minimumBlockHeaderHistory (_chainwebVersion headerDb) trgHeadHeight of
504+
Nothing ->
505+
minTrgHeight
506+
Just minHist -> do
507+
-- if there is a block header history limit by now, then any
508+
-- proof with a target outside of that window will not be
509+
-- usable. so ideally, the proof we produce is in that
510+
-- window. that is no guarantee that such a proof is small
511+
-- enough to store on-chain, if the subject is far in the
512+
-- past, but it's better to try than do nothing.
513+
let trgLowestKnownHeight = max trgHeadHeight (int minHist) - int minHist
514+
-- we don't use the lowest known height directly, we try to
515+
-- increase it by a few block heights to avoid the proof
516+
-- immediately expiring before it can be used.
517+
let trgLowestSafeHeight = min trgHeadHeight (trgLowestKnownHeight + proofSlack)
518+
max minTrgHeight trgLowestSafeHeight
496519
seekAncestor trgChain trgHeadHeader trgHeight >>= \case
497520
Just x -> return $! x
498521
Nothing -> throwM $ SpvExceptionTargetNotReachable
@@ -504,12 +527,11 @@ minimumTrgHeader headerDb tcid scid bh = do
504527
}
505528
where
506529
trgChain = headerDb ^?! ixg tcid
507-
trgHeight
530+
minTrgHeight
508531
| srcGraph == trgGraph = int bh + int srcDistance
509532
| otherwise = int bh + int srcDistance + int trgDistance
510533
-- This assumes that graph changes are at least graph-diameter
511534
-- blocks appart.
512-
513535
srcGraph = chainGraphAt headerDb bh
514536
srcDistance = length $ shortestPath tcid scid srcGraph
515537
trgGraph = chainGraphAt headerDb (bh + int srcDistance)

test/unit/Chainweb/Test/Pact5/RemotePactTest.hs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -363,44 +363,65 @@ spvExpirationTest baseRdb _step = runResourceT $ do
363363
send fx v srcChain [initiator]
364364
let initiatorReqKey = cmdToRequestKey initiator
365365

366-
-- You have to wait at least N blocks before attempting to run the continuation,
366+
-- You have to wait at least a diameter to get the block.
367367
-- where N is the diameter of the graph + some constant. Here, we just run 10 blocks,
368368
-- because it is less than the minimumBlockHeaderHistory of the test version, but
369369
-- more than sufficient for the target chain to be aware of the source xchain transfer.
370370
let waitBlocks :: Integral a => a
371-
waitBlocks = 10
371+
waitBlocks = int $ diameter (chainGraphAt v maxBound) + 1
372372
let expirationWindow = fromMaybe (error "missing minimumBlockHeaderHistory") (minimumBlockHeaderHistory v maxBound)
373373
when (int expirationWindow < waitBlocks + diameter (chainGraphAt v maxBound)) $ assertFailure "test version has a minimumBlockHeaderHistory that is too short to test"
374374

375375
replicateM_ waitBlocks $ advanceAllChains_ fx
376+
376377
[Just sendCr] <- pollWithDepth fx v srcChain [initiatorReqKey] (Just (ConfirmationDepth 0))
377378
let cont = fromMaybe (error "missing continuation") (_crContinuation sendCr)
378-
TransactionOutputProofB64 spvProof <- spvTxOutProof fx v targetChain srcChain initiatorReqKey
379-
let contMsg = ContMsg
379+
380+
TransactionOutputProofB64 expiringSpvProof <- spvTxOutProof fx v targetChain srcChain initiatorReqKey
381+
let expiringContMsg = ContMsg
380382
{ _cmPactId = _peDefPactId cont
381383
, _cmStep = succ $ _peStep cont
382384
, _cmRollback = _peStepHasRollback cont
383385
, _cmData = PUnit
384-
, _cmProof = Just (ContProof (T.encodeUtf8 spvProof))
386+
, _cmProof = Just (ContProof (T.encodeUtf8 expiringSpvProof))
385387
}
386388

387-
-- We have already run an extra 10 blocks since initiating the transfer.
388389
-- We need to wait for the full window to expire.
389-
390390
-- still accessible at remainingWindow + diameter + 1 so we have to go one beyond
391391
let remainingWindow = int @_ @Int (int expirationWindow - waitBlocks + diameter (chainGraphAt v maxBound) + 2)
392392
replicateM_ remainingWindow $ advanceAllChains_ fx
393393

394+
expiringRecv <- buildTextCmd v
395+
$ set cbRPC (mkCont expiringContMsg)
396+
$ defaultCmd targetChain
397+
send fx v targetChain [expiringRecv]
398+
let expiringRecvReqKey = cmdToRequestKey expiringRecv
399+
advanceAllChains_ fx
400+
poll fx v targetChain [expiringRecvReqKey]
401+
>>= P.match (_head . _Just)
402+
? P.checkAll
403+
[ P.fun _crResult ? P.match _PactResultErr ? P.fun _peMsg ? P.equals "Continuation error: spv verification failed: target header is not in the chain or is out of bounds"
404+
]
405+
406+
TransactionOutputProofB64 spvWorkingProof <- spvTxOutProof fx v targetChain srcChain initiatorReqKey
407+
let currentContMsg = ContMsg
408+
{ _cmPactId = _peDefPactId cont
409+
, _cmStep = succ $ _peStep cont
410+
, _cmRollback = _peStepHasRollback cont
411+
, _cmData = PUnit
412+
, _cmProof = Just (ContProof (T.encodeUtf8 spvWorkingProof))
413+
}
414+
394415
recv <- buildTextCmd v
395-
$ set cbRPC (mkCont contMsg)
416+
$ set cbRPC (mkCont currentContMsg)
396417
$ defaultCmd targetChain
397418
send fx v targetChain [recv]
398419
let recvReqKey = cmdToRequestKey recv
399420
advanceAllChains_ fx
400421
poll fx v targetChain [recvReqKey]
401422
>>= P.match (_head . _Just)
402423
? P.checkAll
403-
[ P.fun _crResult ? P.match _PactResultErr ? P.fun _peMsg ? P.equals "Continuation error: spv verification failed: target header is not in the chain or is out of bounds"
424+
[ P.fun _crResult ? P.match _PactResultOk P.succeed
404425
]
405426

406427

0 commit comments

Comments
 (0)