A very experimental PLC implementation which uses BFT consensus for decentralization

Begin work on validator trust mechanism

gbl08ma.com c64bc7d9 a3fe0f8c

verified
+882 -96
+29 -8
abciapp/app.go
··· 9 9 10 10 dbm "github.com/cometbft/cometbft-db" 11 11 abcitypes "github.com/cometbft/cometbft/abci/types" 12 + bftstore "github.com/cometbft/cometbft/store" 13 + "github.com/cometbft/cometbft/types" 12 14 "github.com/cosmos/iavl" 13 15 "github.com/klauspost/compress/zstd" 14 16 "github.com/palantir/stacktrace" ··· 28 30 tree *iavl.MutableTree 29 31 fullyClearApplicationData func() error 30 32 33 + privValidator types.PrivValidator 34 + 31 35 ongoingRead transaction.Read 32 36 ongoingWrite transaction.Write 33 37 ··· 38 42 lastProcessedProposalExecTxResults []*processResult 39 43 40 44 aocsByPLC map[string]*authoritativeOperationsCache 45 + 46 + blockChallengeCoordinator *blockChallengeCoordinator 41 47 } 42 48 43 49 // store and plc must be able to share transaction objects 44 - func NewDIDPLCApplication(treeDB dbm.DB, indexDB transaction.ExtendedDB, clearData func(), snapshotDirectory, didBloomFilterPath string) (*DIDPLCApplication, *transaction.Factory, plc.PLC, func(), error) { 50 + func NewDIDPLCApplication(pv types.PrivValidator, treeDB dbm.DB, indexDB transaction.ExtendedDB, clearData func(), snapshotDirectory, didBloomFilterPath string) (*DIDPLCApplication, *transaction.Factory, plc.PLC, func(), error) { 45 51 mkTree := func() *iavl.MutableTree { 46 52 // Using SpeedDefault appears to cause the processing time for ExecuteOperation to double on average 47 53 // Using SpeedBetterCompression appears to cause the processing time to double again ··· 62 68 return nil, nil, nil, func() {}, stacktrace.Propagate(err, "") 63 69 } 64 70 } 71 + 72 + runnerContext, cancelRunnerContext := context.WithCancel(context.Background()) 65 73 66 74 d := &DIDPLCApplication{ 67 - runnerContext: context.Background(), 75 + runnerContext: runnerContext, 76 + privValidator: pv, 68 77 tree: tree, 69 78 indexDB: indexDB, 70 79 snapshotDirectory: snapshotDirectory, 71 80 aocsByPLC: make(map[string]*authoritativeOperationsCache), 72 81 } 73 82 74 - d.txFactory, err = transaction.NewFactory(tree, indexDB, store.Consensus.NextOperationSequence, store.NewDIDBloomFilterStore(didBloomFilterPath)) 83 + d.txFactory, err = transaction.NewFactory(tree, indexDB, store.Consensus.CountOperations, store.NewDIDBloomFilterStore(didBloomFilterPath)) 75 84 if err != nil { 76 - return nil, nil, nil, func() {}, stacktrace.Propagate(err, "") 85 + return nil, nil, nil, cancelRunnerContext, stacktrace.Propagate(err, "") 77 86 } 78 87 79 88 d.fullyClearApplicationData = func() error { ··· 88 97 89 98 *d.tree = *mkTree() 90 99 91 - d.txFactory, err = transaction.NewFactory(tree, indexDB, store.Consensus.NextOperationSequence, store.NewDIDBloomFilterStore(didBloomFilterPath)) 100 + d.txFactory, err = transaction.NewFactory(tree, indexDB, store.Consensus.CountOperations, store.NewDIDBloomFilterStore(didBloomFilterPath)) 92 101 if err != nil { 93 102 return stacktrace.Propagate(err, "") 94 103 } ··· 98 107 d.plc = plc.NewPLC() 99 108 100 109 var wg sync.WaitGroup 101 - closeCh := make(chan struct{}) 102 110 wg.Go(func() { 103 111 // periodically store bloom filter so we don't have to wait so long on the next startup 104 112 for { 105 113 select { 106 - case <-closeCh: 114 + case <-runnerContext.Done(): 107 115 return 108 116 case <-time.After(5 * time.Minute): 109 117 } ··· 169 177 */ 170 178 171 179 return d, d.txFactory, d.plc, func() { 172 - close(closeCh) 180 + cancelRunnerContext() 173 181 wg.Wait() 174 182 lo.Must0(d.tree.Close()) 175 183 }, nil 184 + } 185 + 186 + func (d *DIDPLCApplication) FinishInitializing(blockStore *bftstore.BlockStore) error { 187 + pubKey, err := d.privValidator.GetPubKey() 188 + if err != nil { 189 + return stacktrace.Propagate(err, "") 190 + } 191 + 192 + d.blockChallengeCoordinator, err = newBlockChallengeCoordinator(d.runnerContext, d.txFactory, blockStore, pubKey) 193 + if err != nil { 194 + return stacktrace.Propagate(err, "") 195 + } 196 + return nil 176 197 } 177 198 178 199 var _ abcitypes.Application = (*DIDPLCApplication)(nil)
+1 -1
abciapp/app_test.go
··· 22 22 } 23 23 24 24 func TestCheckTx(t *testing.T) { 25 - app, _, _, cleanup, err := abciapp.NewDIDPLCApplication(dbm.NewMemDB(), memDBWrapper{dbm.NewMemDB()}, nil, "", "") 25 + app, _, _, cleanup, err := abciapp.NewDIDPLCApplication(nil, dbm.NewMemDB(), memDBWrapper{dbm.NewMemDB()}, nil, "", "") 26 26 require.NoError(t, err) 27 27 t.Cleanup(cleanup) 28 28
+320
abciapp/block_challenge.go
··· 1 + package abciapp 2 + 3 + import ( 4 + "bytes" 5 + "context" 6 + "embed" 7 + "fmt" 8 + "math/big" 9 + "time" 10 + 11 + "github.com/Yiling-J/theine-go" 12 + "github.com/cometbft/cometbft/crypto" 13 + bftstore "github.com/cometbft/cometbft/store" 14 + "github.com/consensys/gnark-crypto/ecc" 15 + "github.com/consensys/gnark-crypto/ecc/bn254" 16 + "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" 17 + "github.com/consensys/gnark/backend" 18 + "github.com/consensys/gnark/backend/groth16" 19 + "github.com/consensys/gnark/backend/witness" 20 + "github.com/consensys/gnark/constraint" 21 + "github.com/consensys/gnark/constraint/solver" 22 + "github.com/consensys/gnark/frontend" 23 + "github.com/palantir/stacktrace" 24 + "github.com/rs/zerolog" 25 + "github.com/samber/lo" 26 + "resenje.org/singleflight" 27 + "tangled.org/gbl08ma.com/didplcbft/proof" 28 + "tangled.org/gbl08ma.com/didplcbft/store" 29 + "tangled.org/gbl08ma.com/didplcbft/transaction" 30 + ) 31 + 32 + //go:embed proofcircuit/BlockChallenge_* 33 + var blockChallengeCircuitFS embed.FS 34 + 35 + var blockChallengeConstraintSystem constraint.ConstraintSystem 36 + var blockChallengeProvingKey groth16.ProvingKey 37 + var blockChallengeVerifyingKey groth16.VerifyingKey 38 + 39 + func init() { 40 + blockChallengeConstraintSystem = groth16.NewCS(ecc.BN254) 41 + csFile := lo.Must(blockChallengeCircuitFS.Open("proofcircuit/BlockChallenge_ConstraintSystem")) 42 + defer csFile.Close() 43 + lo.Must(blockChallengeConstraintSystem.ReadFrom(csFile)) 44 + 45 + blockChallengeProvingKey = groth16.NewProvingKey(ecc.BN254) 46 + pkFile := lo.Must(blockChallengeCircuitFS.Open("proofcircuit/BlockChallenge_ProvingKey")) 47 + defer pkFile.Close() 48 + lo.Must(blockChallengeProvingKey.ReadFrom(pkFile)) 49 + 50 + blockChallengeVerifyingKey = groth16.NewVerifyingKey(ecc.BN254) 51 + vkFile := lo.Must(blockChallengeCircuitFS.Open("proofcircuit/BlockChallenge_VerifyingKey")) 52 + defer vkFile.Close() 53 + lo.Must(blockChallengeVerifyingKey.ReadFrom(vkFile)) 54 + } 55 + 56 + type blockChallengeCoordinator struct { 57 + g singleflight.Group[int64, []byte] 58 + 59 + runnerContext context.Context 60 + 61 + validatorAddress []byte 62 + txFactory *transaction.Factory 63 + nodeBlockStore *bftstore.BlockStore 64 + 65 + sharedWitnessDataCache *theine.LoadingCache[int64, proof.BlockChallengeCircuit] 66 + } 67 + 68 + func newBlockChallengeCoordinator(runnerContext context.Context, txFactory *transaction.Factory, blockStore *bftstore.BlockStore, pubKey crypto.PubKey) (*blockChallengeCoordinator, error) { 69 + c := &blockChallengeCoordinator{ 70 + runnerContext: runnerContext, 71 + txFactory: txFactory, 72 + nodeBlockStore: blockStore, 73 + validatorAddress: pubKey.Address(), 74 + } 75 + 76 + var err error 77 + c.sharedWitnessDataCache, err = theine.NewBuilder[int64, proof.BlockChallengeCircuit](5). 78 + Loading( 79 + func(ctx context.Context, height int64) (theine.Loaded[proof.BlockChallengeCircuit], error) { 80 + var zeroValue theine.Loaded[proof.BlockChallengeCircuit] 81 + 82 + anyTx := ctx.Value(contextTxKey{}) 83 + if anyTx == nil { 84 + return theine.Loaded[proof.BlockChallengeCircuit]{}, stacktrace.NewError("transaction not found in context") 85 + } 86 + tx, ok := anyTx.(transaction.Read) 87 + if !ok { 88 + return zeroValue, stacktrace.NewError("invalid transaction in context") 89 + } 90 + 91 + operationData, lastCommitHash, err := c.blockChallengeOperationDataForHeight(tx, height) 92 + if err != nil { 93 + return zeroValue, stacktrace.Propagate(err, "") 94 + } 95 + 96 + sharedPart := buildBlockChallengeCircuitAssignmentShared(lastCommitHash, [1550]byte(operationData)) 97 + 98 + return theine.Loaded[proof.BlockChallengeCircuit]{ 99 + Value: sharedPart, 100 + }, nil 101 + }).Build() 102 + if err != nil { 103 + return nil, stacktrace.Propagate(err, "") 104 + } 105 + 106 + return c, nil 107 + } 108 + 109 + type contextTxKey struct{} 110 + 111 + // in challengeManager method arguments, `height` is always the height of the "current" block C which uses data from block C-1: 112 + // - the consensus tree from block C-1 113 + // - the lastCommitHash from block C-1 114 + // to solve/prove the challenge for block C, which gets stored as the challenge for block C 115 + // C might be the height of the block that is currently still being prepared/proposed/voted on 116 + // the proofs should be stable regardless of how many proposal rounds we go through, because they only use data from C-1 117 + 118 + func (c *blockChallengeCoordinator) notifyOfIncomingBlockHeight(height int64) { 119 + go func() { 120 + _, err := c.loadOrComputeBlockChallengeProof(c.runnerContext, height) 121 + if err != nil { 122 + fmt.Printf("FAILED TO COMPUTE CHALLENGE FOR BLOCK %d: %v\n", height, stacktrace.Propagate(err, "")) 123 + } 124 + }() 125 + } 126 + 127 + func (c *blockChallengeCoordinator) loadOrComputeBlockChallengeProof(ctx context.Context, height int64) ([]byte, error) { 128 + proof, _, err := c.g.Do(ctx, height, func(ctx context.Context) ([]byte, error) { 129 + // we need to read the tree as it was on the block prior. We assume that this method will only be called in the processing of the latest block 130 + // and we validate this assumption 131 + tx := c.txFactory.ReadCommitted() 132 + if tx.Height() != height-1 { 133 + return nil, stacktrace.NewError("challenge being loaded or computed for unexpected height %d, expected %d", height, tx.Height()+1) 134 + } 135 + 136 + proof, err := store.Consensus.BlockChallengeProof(tx, uint64(height)) 137 + if err != nil { 138 + return nil, stacktrace.Propagate(err, "") 139 + } 140 + if proof == nil { 141 + // compute and store 142 + proof, err = c.computeBlockChallengeProof(tx, height) 143 + if err != nil { 144 + return nil, stacktrace.Propagate(err, "") 145 + } 146 + 147 + wtx, err := tx.UpgradeForIndexOnly() 148 + if err != nil { 149 + return nil, stacktrace.Propagate(err, "") 150 + } 151 + defer wtx.Rollback() 152 + 153 + err = store.Consensus.StoreBlockChallengeProof(ctx, wtx, uint64(height), proof) 154 + if err != nil { 155 + return nil, stacktrace.Propagate(err, "") 156 + } 157 + 158 + err = wtx.Commit() 159 + if err != nil { 160 + return nil, stacktrace.Propagate(err, "") 161 + } 162 + } 163 + return proof, nil 164 + }) 165 + return proof, stacktrace.Propagate(err, "") 166 + } 167 + 168 + func (c *blockChallengeCoordinator) computeBlockChallengeProof(tx transaction.Read, height int64) ([]byte, error) { 169 + st := time.Now() 170 + 171 + witness, err := c.buildPrivateChallengeWitnessForHeight(tx, height) 172 + if err != nil { 173 + return nil, stacktrace.Propagate(err, "") 174 + } 175 + 176 + // TODO consider using a different logger once we clean up our logging act 177 + // TODO open an issue in the gnark repo because backend.WithSolverOptions(solver.WithLogger(zerolog.Nop())) has no effect... 178 + proof, err := groth16.Prove(blockChallengeConstraintSystem, blockChallengeProvingKey, witness, backend.WithSolverOptions(solver.WithLogger(zerolog.Nop()))) 179 + if err != nil { 180 + return nil, stacktrace.Propagate(err, "") 181 + } 182 + 183 + buf := new(bytes.Buffer) 184 + 185 + _, err = proof.WriteTo(buf) 186 + if err != nil { 187 + return nil, stacktrace.Propagate(err, "") 188 + } 189 + 190 + fmt.Println("COMPUTED CHALLENGE FOR BLOCK", height, "IN", time.Since(st), "SIZE", buf.Len(), "BYTES") 191 + 192 + return buf.Bytes(), nil 193 + } 194 + 195 + func (c *blockChallengeCoordinator) verifyBlockChallengeProof(height int64, validatorAddress []byte, proofBytes []byte) (bool, error) { 196 + tx := c.txFactory.ReadCommitted() 197 + if tx.Height() != height-1 { 198 + return false, stacktrace.NewError("challenge being verified for unexpected height %d, expected %d", height, tx.Height()+1) 199 + } 200 + 201 + sharedPart, err := c.fetchOrBuildBlockChallengeCircuitAssignmentShared(tx, height) 202 + if err != nil { 203 + return false, stacktrace.Propagate(err, "") 204 + } 205 + 206 + assignment := buildBlockChallengeCircuitAssignmentFull(sharedPart, validatorAddress) 207 + 208 + witness, err := frontend.NewWitness(assignment, bn254.ID.ScalarField()) 209 + if err != nil { 210 + return false, stacktrace.Propagate(err, "") 211 + } 212 + 213 + publicWitness, err := witness.Public() 214 + if err != nil { 215 + return false, stacktrace.Propagate(err, "") 216 + } 217 + 218 + proof := groth16.NewProof(bn254.ID) 219 + 220 + _, err = proof.ReadFrom(bytes.NewBuffer(proofBytes)) 221 + if err != nil { 222 + return false, stacktrace.Propagate(err, "") 223 + } 224 + 225 + err = groth16.Verify(proof, blockChallengeVerifyingKey, publicWitness) 226 + return err == nil, nil 227 + } 228 + 229 + func (c *blockChallengeCoordinator) buildPrivateChallengeWitnessForHeight(tx transaction.Read, height int64) (witness.Witness, error) { 230 + sharedPart, err := c.fetchOrBuildBlockChallengeCircuitAssignmentShared(tx, height) 231 + if err != nil { 232 + return nil, stacktrace.Propagate(err, "") 233 + } 234 + 235 + assignment := buildBlockChallengeCircuitAssignmentFull(sharedPart, c.validatorAddress) 236 + 237 + witness, err := frontend.NewWitness(assignment, bn254.ID.ScalarField()) 238 + return witness, stacktrace.Propagate(err, "") 239 + } 240 + 241 + func (c *blockChallengeCoordinator) blockChallengeOperationDataForHeight(tx transaction.Read, height int64) ([]byte, []byte, error) { 242 + if height <= 1 { 243 + return make([]byte, proof.OperationDataLength), make([]byte, 32), nil 244 + } 245 + lastBlock := c.nodeBlockStore.LoadBlock(height - 1) 246 + if lastBlock == nil { 247 + return nil, nil, stacktrace.NewError("height not found") 248 + } 249 + 250 + lastCommitHash := lastBlock.LastCommitHash 251 + lastCommitHashBigInt := big.NewInt(0).SetBytes(lastCommitHash) 252 + 253 + highestOp, err := tx.CountOperations() 254 + if err != nil { 255 + return nil, nil, stacktrace.Propagate(err, "") 256 + } 257 + 258 + initialOpDataLen := proof.OperationDataLength + int(lastCommitHash[0]) 259 + operationData := make([]byte, initialOpDataLen) 260 + operationDataCursor := 0 261 + if highestOp > 0 { 262 + startOpIdxBigInt := big.NewInt(0).Mod(lastCommitHashBigInt, big.NewInt(0).SetUint64(highestOp-1)) 263 + startOpIdx := startOpIdxBigInt.Uint64() 264 + // the starting operation sequence is startOpIdx+1 265 + // because operations sequences start at 1 but the result of the modulus is (0, n( 266 + // but that's ok because the OperationsIterator cursor parameter is "after" (i.e. the lower bound is exclusive) 267 + // so we indeed want a 0-indexed parameter 268 + for _, rawOpValue := range store.Consensus.OperationsIterator(tx, startOpIdx, &err) { 269 + operationDataCursor += copy(operationData[operationDataCursor:], rawOpValue) 270 + if operationDataCursor >= initialOpDataLen { 271 + break 272 + } 273 + } 274 + if err != nil { 275 + return nil, nil, stacktrace.Propagate(err, "") 276 + } 277 + } 278 + 279 + return operationData[lastCommitHash[0]:], lastBlock.LastCommitHash, nil 280 + } 281 + 282 + func (c *blockChallengeCoordinator) fetchOrBuildBlockChallengeCircuitAssignmentShared(tx transaction.Read, height int64) (proof.BlockChallengeCircuit, error) { 283 + ctx := context.WithValue(c.runnerContext, contextTxKey{}, tx) 284 + v, err := c.sharedWitnessDataCache.Get(ctx, height) 285 + return v, stacktrace.Propagate(err, "") 286 + } 287 + 288 + func buildBlockChallengeCircuitAssignmentShared(lastCommitHash []byte, data [1550]byte) proof.BlockChallengeCircuit { 289 + var assignment proof.BlockChallengeCircuit 290 + 291 + h := mimc.NewMiMC() 292 + 293 + if len(lastCommitHash) > 31 { 294 + lastCommitHash = lastCommitHash[len(lastCommitHash)-31:] 295 + } 296 + h.Write(lastCommitHash) 297 + assignment.LastCommitHash = lastCommitHash 298 + 299 + for i := 0; i < len(assignment.OperationData); i++ { 300 + d := data[31*i : 31*(i+1)] 301 + assignment.OperationData[i] = d 302 + h.Write(d) 303 + } 304 + 305 + sharedHash := h.Sum(nil) 306 + assignment.SharedHash = sharedHash 307 + return assignment 308 + } 309 + 310 + func buildBlockChallengeCircuitAssignmentFull(shared proof.BlockChallengeCircuit, validatorAddress []byte) *proof.BlockChallengeCircuit { 311 + h := mimc.NewMiMC() 312 + 313 + h.Write(validatorAddress) 314 + h.Write(shared.SharedHash.([]byte)) 315 + 316 + shared.ValidatorAddress = validatorAddress 317 + shared.SpecificHash = h.Sum(nil) 318 + 319 + return &shared 320 + }
+31 -9
abciapp/execution.go
··· 16 16 ) 17 17 18 18 // InitChain implements [types.Application]. 19 - func (d *DIDPLCApplication) InitChain(context.Context, *abcitypes.RequestInitChain) (*abcitypes.ResponseInitChain, error) { 20 - // TODO 21 - return &abcitypes.ResponseInitChain{}, nil 19 + func (d *DIDPLCApplication) InitChain(_ context.Context, req *abcitypes.RequestInitChain) (*abcitypes.ResponseInitChain, error) { 20 + req.ConsensusParams.Abci.VoteExtensionsEnableHeight = 1 21 + return &abcitypes.ResponseInitChain{ 22 + ConsensusParams: req.ConsensusParams, 23 + }, nil 22 24 } 23 25 24 26 // PrepareProposal implements [types.Application]. ··· 124 126 } 125 127 }() 126 128 129 + if d.privValidator != nil { 130 + d.blockChallengeCoordinator.notifyOfIncomingBlockHeight(req.Height) 131 + } 132 + 127 133 txResults := make([]*processResult, len(req.Txs)) 128 134 for i, tx := range req.Txs { 129 135 result, action, processor, err := beginProcessTx(tx) ··· 160 166 } 161 167 162 168 // ExtendVote implements [types.Application]. 163 - func (d *DIDPLCApplication) ExtendVote(context.Context, *abcitypes.RequestExtendVote) (*abcitypes.ResponseExtendVote, error) { 164 - // TODO 165 - return &abcitypes.ResponseExtendVote{}, nil 169 + func (d *DIDPLCApplication) ExtendVote(ctx context.Context, req *abcitypes.RequestExtendVote) (*abcitypes.ResponseExtendVote, error) { 170 + if d.privValidator == nil { 171 + // we are not meant to be performing validator duties... 172 + return nil, stacktrace.NewError("unexpected ExtendVote call: node is not configured to be a validator") 173 + } 174 + 175 + proof, err := d.blockChallengeCoordinator.loadOrComputeBlockChallengeProof(ctx, req.Height) 176 + if err != nil { 177 + return nil, stacktrace.Propagate(err, "") 178 + } 179 + return &abcitypes.ResponseExtendVote{ 180 + VoteExtension: proof, 181 + }, nil 166 182 } 167 183 168 184 // VerifyVoteExtension implements [types.Application]. 169 - func (d *DIDPLCApplication) VerifyVoteExtension(context.Context, *abcitypes.RequestVerifyVoteExtension) (*abcitypes.ResponseVerifyVoteExtension, error) { 170 - // TODO 171 - return &abcitypes.ResponseVerifyVoteExtension{}, nil 185 + func (d *DIDPLCApplication) VerifyVoteExtension(_ context.Context, req *abcitypes.RequestVerifyVoteExtension) (*abcitypes.ResponseVerifyVoteExtension, error) { 186 + proofOK, err := d.blockChallengeCoordinator.verifyBlockChallengeProof(req.Height, req.ValidatorAddress, req.VoteExtension) 187 + if err != nil { 188 + return nil, stacktrace.Propagate(err, "") 189 + } 190 + 191 + return &abcitypes.ResponseVerifyVoteExtension{ 192 + Status: lo.Ternary(proofOK, abcitypes.ResponseVerifyVoteExtension_ACCEPT, abcitypes.ResponseVerifyVoteExtension_REJECT), 193 + }, nil 172 194 } 173 195 174 196 // FinalizeBlock implements [types.Application].
abciapp/proofcircuit/BlockChallengeCircuit_ConstraintSystem

This is a binary file and will not be displayed.

abciapp/proofcircuit/BlockChallengeCircuit_ProvingKey

This is a binary file and will not be displayed.

abciapp/proofcircuit/BlockChallengeCircuit_VerifyingKey

This is a binary file and will not be displayed.

abciapp/proofcircuit/BlockChallenge_ConstraintSystem

This is a binary file and will not be displayed.

abciapp/proofcircuit/BlockChallenge_ProvingKey

This is a binary file and will not be displayed.

abciapp/proofcircuit/BlockChallenge_VerifyingKey

This is a binary file and will not be displayed.

-4
dbmtoiavldb/adapter.go
··· 73 73 } 74 74 75 75 func (i *AdaptedIterator) Next() { 76 - /*if !i.calledNextOnce { 77 - i.calledNextOnce = true 78 - return 79 - }*/ 80 76 i.underlying.Next() 81 77 } 82 78
+15 -2
go.mod
··· 4 4 5 5 require ( 6 6 cosmossdk.io/core v0.12.1-0.20240725072823-6a2d039e1212 7 + github.com/Yiling-J/theine-go v0.6.2 7 8 github.com/bits-and-blooms/bloom/v3 v3.7.1 8 9 github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe 9 10 github.com/cometbft/cometbft v0.38.19 10 11 github.com/cometbft/cometbft-db v0.14.1 12 + github.com/consensys/gnark v0.14.0 13 + github.com/consensys/gnark-crypto v0.19.0 11 14 github.com/cosmos/iavl v1.3.5 12 15 github.com/cosmos/ics23/go v0.10.0 13 16 github.com/dgraph-io/badger/v4 v4.9.0 ··· 20 23 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f 21 24 github.com/puzpuzpuz/xsync/v4 v4.2.0 22 25 github.com/rs/cors v1.11.1 26 + github.com/rs/zerolog v1.34.0 23 27 github.com/samber/lo v1.52.0 24 28 github.com/samber/mo v1.16.0 25 29 github.com/spf13/viper v1.19.0 26 30 github.com/stretchr/testify v1.11.1 27 31 github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb 32 + resenje.org/singleflight v0.4.3 28 33 ) 29 34 30 35 require ( 31 36 github.com/DataDog/zstd v1.4.5 // indirect 32 37 github.com/beorn7/perks v1.0.1 // indirect 33 38 github.com/bits-and-blooms/bitset v1.24.2 // indirect 39 + github.com/blang/semver/v4 v4.0.0 // indirect 34 40 github.com/cespare/xxhash/v2 v2.3.0 // indirect 35 41 github.com/cockroachdb/errors v1.11.3 // indirect 36 42 github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect ··· 45 51 github.com/dustin/go-humanize v1.0.1 // indirect 46 52 github.com/emicklei/dot v1.6.2 // indirect 47 53 github.com/fsnotify/fsnotify v1.7.0 // indirect 54 + github.com/fxamacker/cbor/v2 v2.9.0 // indirect 48 55 github.com/getsentry/sentry-go v0.27.0 // indirect 49 56 github.com/go-kit/kit v0.13.0 // indirect 50 57 github.com/go-kit/log v0.2.1 // indirect ··· 58 65 github.com/google/flatbuffers v25.2.10+incompatible // indirect 59 66 github.com/google/go-cmp v0.7.0 // indirect 60 67 github.com/google/orderedcode v0.0.1 // indirect 68 + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 // indirect 61 69 github.com/gorilla/websocket v1.5.3 // indirect 62 70 github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 63 71 github.com/hashicorp/hcl v1.0.0 // indirect 72 + github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 // indirect 64 73 github.com/ipfs/go-block-format v0.2.0 // indirect 65 74 github.com/ipfs/go-ipfs-util v0.0.3 // indirect 66 75 github.com/ipfs/go-ipld-format v0.6.0 // indirect ··· 71 80 github.com/lib/pq v1.10.9 // indirect 72 81 github.com/linxGnu/grocksdb v1.8.14 // indirect 73 82 github.com/magiconair/properties v1.8.7 // indirect 83 + github.com/mattn/go-colorable v0.1.14 // indirect 84 + github.com/mattn/go-isatty v0.0.20 // indirect 74 85 github.com/minio/highwayhash v1.0.3 // indirect 75 86 github.com/minio/sha256-simd v1.0.1 // indirect 76 87 github.com/mitchellh/mapstructure v1.5.0 // indirect ··· 92 103 github.com/prometheus/procfs v0.15.1 // indirect 93 104 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect 94 105 github.com/rogpeppe/go-internal v1.13.1 // indirect 106 + github.com/ronanh/intcomp v1.1.1 // indirect 95 107 github.com/sagikazarmark/locafero v0.4.0 // indirect 96 108 github.com/sagikazarmark/slog-shim v0.1.0 // indirect 97 109 github.com/sasha-s/go-deadlock v0.3.5 // indirect ··· 103 115 github.com/subosito/gotenv v1.6.0 // indirect 104 116 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect 105 117 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect 118 + github.com/x448/float16 v0.8.4 // indirect 119 + github.com/zeebo/xxh3 v1.0.2 // indirect 106 120 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 107 121 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 108 122 go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect ··· 112 126 go.opentelemetry.io/otel/trace v1.37.0 // indirect 113 127 go.uber.org/multierr v1.11.0 // indirect 114 128 golang.org/x/crypto v0.41.0 // indirect 115 - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect 129 + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect 116 130 golang.org/x/net v0.43.0 // indirect 117 131 golang.org/x/sync v0.16.0 // indirect 118 132 golang.org/x/sys v0.36.0 // indirect 119 133 golang.org/x/text v0.28.0 // indirect 120 - golang.org/x/tools v0.36.0 // indirect 121 134 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 122 135 google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect 123 136 google.golang.org/grpc v1.70.0 // indirect
+45 -2
go.sum
··· 11 11 github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= 12 12 github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= 13 13 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 14 + github.com/Yiling-J/theine-go v0.6.2 h1:1GeoXeQ0O0AUkiwj2S9Jc0Mzx+hpqzmqsJ4kIC4M9AY= 15 + github.com/Yiling-J/theine-go v0.6.2/go.mod h1:08QpMa5JZ2pKN+UJCRrCasWYO1IKCdl54Xa836rpmDU= 14 16 github.com/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= 15 17 github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= 16 18 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= ··· 19 21 github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 20 22 github.com/bits-and-blooms/bloom/v3 v3.7.1 h1:WXovk4TRKZttAMJfoQx6K2DM0zNIt8w+c67UqO+etV0= 21 23 github.com/bits-and-blooms/bloom/v3 v3.7.1/go.mod h1:rZzYLLje2dfzXfAkJNxQQHsKurAyK55KUnL43Euk0hU= 24 + github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 25 + github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 22 26 github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe h1:VBhaqE5ewQgXbY5SfSWFZC/AwHFo7cHxZKFYi2ce9Yo= 23 27 github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe/go.mod h1:RuQVrCGm42QNsgumKaR6se+XkFKfCPNwdCiTvqKRUck= 24 28 github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= ··· 45 49 github.com/cometbft/cometbft v0.38.19/go.mod h1:UCu8dlHqvkAsmAFmWDRWNZJPlu6ya2fTWZlDrWsivwo= 46 50 github.com/cometbft/cometbft-db v0.14.1 h1:SxoamPghqICBAIcGpleHbmoPqy+crij/++eZz3DlerQ= 47 51 github.com/cometbft/cometbft-db v0.14.1/go.mod h1:KHP1YghilyGV/xjD5DP3+2hyigWx0WTp9X+0Gnx0RxQ= 52 + github.com/consensys/gnark v0.14.0 h1:RG+8WxRanFSFBSlmCDRJnYMYYKpH3Ncs5SMzg24B5HQ= 53 + github.com/consensys/gnark v0.14.0/go.mod h1:1IBpDPB/Rdyh55bQRR4b0z1WvfHQN1e0020jCvKP2Gk= 54 + github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA= 55 + github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= 48 56 github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= 49 57 github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= 58 + github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 50 59 github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= 51 60 github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= 52 61 github.com/cosmos/iavl v1.3.5 h1:wTDFbaa/L0FVUrwTlzMnjN3fphtKgWxgcZmTc45MZuA= ··· 87 96 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 88 97 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 89 98 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 99 + github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= 100 + github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 90 101 github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= 91 102 github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= 92 103 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= ··· 103 114 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 104 115 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 105 116 github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 117 + github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 106 118 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 107 119 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 108 120 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= ··· 129 141 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 130 142 github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= 131 143 github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= 144 + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= 145 + github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= 132 146 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 133 147 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 134 148 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= ··· 142 156 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 143 157 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 144 158 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 159 + github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2 h1:B+aWVgAx+GlFLhtYjIaF0uGjU3rzpl99Wf9wZWt+Mq8= 160 + github.com/ingonyama-zk/icicle-gnark/v3 v3.2.2/go.mod h1:CH/cwcr21pPWH+9GtK/PFaa4OGTv4CtfkCKro6GpbRE= 145 161 github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= 146 162 github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= 147 163 github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= ··· 168 184 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 169 185 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 170 186 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 187 + github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= 188 + github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= 171 189 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 172 190 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 173 191 github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ= 174 192 github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= 175 193 github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 176 194 github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 195 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 196 + github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 197 + github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 198 + github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 199 + github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 200 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 201 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 177 202 github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= 178 203 github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= 179 204 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= ··· 243 268 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 244 269 github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 245 270 github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 271 + github.com/ronanh/intcomp v1.1.1 h1:+1bGV/wEBiHI0FvzS7RHgzqOpfbBJzLIxkqMJ9e6yxY= 272 + github.com/ronanh/intcomp v1.1.1/go.mod h1:7FOLy3P3Zj3er/kVrU/pl+Ql7JFZj7bwliMGketo0IU= 246 273 github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= 247 274 github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= 275 + github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 276 + github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= 277 + github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= 248 278 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 249 279 github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= 250 280 github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= ··· 300 330 github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= 301 331 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4= 302 332 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 333 + github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 334 + github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 303 335 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 304 336 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 337 + github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= 338 + github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= 339 + github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= 340 + github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= 305 341 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 306 342 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 307 343 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= ··· 327 363 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 328 364 golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 329 365 golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 330 - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= 331 - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= 366 + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= 367 + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= 332 368 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 333 369 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 334 370 golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= ··· 359 395 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 360 396 golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 397 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 398 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 362 399 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 400 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 401 + golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 363 402 golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 364 403 golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= 365 404 golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= ··· 404 443 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 405 444 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 406 445 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 446 + gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 447 + gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 407 448 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 408 449 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 409 450 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 410 451 lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= 411 452 lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 453 + resenje.org/singleflight v0.4.3 h1:l7foFYg8X/VEHPxWs1K/Pw77807RMVzvXgWGb0J1sdM= 454 + resenje.org/singleflight v0.4.3/go.mod h1:lAgQK7VfjG6/pgredbQfmV0RvG/uVhKo6vSuZ0vCWfk=
+11 -6
main.go
··· 90 90 os.Remove(didBloomFilterPath) 91 91 } 92 92 93 - app, txFactory, plc, cleanup, err := abciapp.NewDIDPLCApplication(treeDB, indexDB, recreateDatabases, filepath.Join(homeDir, "snapshots"), didBloomFilterPath) 94 - if err != nil { 95 - log.Fatalf("failed to create DIDPLC application: %v", err) 96 - } 97 - defer cleanup() 98 - 99 93 pv := privval.LoadFilePV( 100 94 config.PrivValidatorKeyFile(), 101 95 config.PrivValidatorStateFile(), 102 96 ) 97 + 98 + app, txFactory, plc, cleanup, err := abciapp.NewDIDPLCApplication(pv, treeDB, indexDB, recreateDatabases, filepath.Join(homeDir, "snapshots"), didBloomFilterPath) 99 + if err != nil { 100 + log.Fatalf("failed to create DIDPLC application: %v", err) 101 + } 102 + defer cleanup() 103 103 104 104 nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) 105 105 if err != nil { ··· 126 126 127 127 if err != nil { 128 128 log.Fatalf("Creating node: %v", err) 129 + } 130 + 131 + err = app.FinishInitializing(node.BlockStore()) 132 + if err != nil { 133 + log.Fatalf("Finishing ABCI app initialization: %v", err) 129 134 } 130 135 131 136 err = node.Start()
+3
plc/plc_test.go
··· 204 204 205 205 require.Equal(t, "bafyreifgafcel2okxszhgbugieyvtmfig2gtf3dgqoh5fvdh3nlh6ncv6q", export[0].Operation.AsOperation().CID().String()) 206 206 require.Equal(t, "bafyreifgafcel2okxszhgbugieyvtmfig2gtf3dgqoh5fvdh3nlh6ncv6q", export[0].CID.String()) 207 + require.Equal(t, uint64(1), export[0].Seq) 207 208 require.Equal(t, "bafyreia6ewwkwjgly6dijfepaq2ey6zximodbtqqi5f6fyugli3cxohn5m", export[1].Operation.AsOperation().CID().String()) 208 209 require.Equal(t, "bafyreia6ewwkwjgly6dijfepaq2ey6zximodbtqqi5f6fyugli3cxohn5m", export[1].CID.String()) 210 + require.Equal(t, uint64(2), export[1].Seq) 209 211 require.Equal(t, "bafyreigyzl2esgnk7nvav5myvgywbshdmatzthc73iiar7tyeq3xjt47m4", export[2].Operation.AsOperation().CID().String()) 210 212 require.Equal(t, "bafyreigyzl2esgnk7nvav5myvgywbshdmatzthc73iiar7tyeq3xjt47m4", export[2].CID.String()) 213 + require.Equal(t, uint64(3), export[2].Seq) 211 214 212 215 // the after parameter is exclusive, with a limit of 1, we should just get the second successful operation 213 216 export, err = testPLC.Export(ctx, txFactory.ReadCommitted(), export[0].Seq, 1)
+41
proof/block_challenge.go
··· 1 + package proof 2 + 3 + import ( 4 + "github.com/consensys/gnark/frontend" 5 + "github.com/consensys/gnark/std/hash/mimc" 6 + ) 7 + 8 + const OperationDataVariables = 50 9 + const OperationDataLength = OperationDataVariables * 31 10 + 11 + type BlockChallengeCircuit struct { 12 + OperationData [OperationDataVariables]frontend.Variable 13 + LastCommitHash frontend.Variable `gnark:",public"` // A unique challenge ID, the same for all validators 14 + SharedHash frontend.Variable `gnark:",public"` // Hash(LastCommitHash, OperationData) 15 + 16 + // Validator-specific: 17 + ValidatorAddress frontend.Variable `gnark:",public"` 18 + SpecificHash frontend.Variable `gnark:",public"` // Hash(ValidatorID, SharedHash) 19 + } 20 + 21 + func (circuit *BlockChallengeCircuit) Define(api frontend.API) error { 22 + mimc, _ := mimc.NewMiMC(api) 23 + 24 + mimc.Write(circuit.LastCommitHash) 25 + 26 + for i := 0; i < len(circuit.OperationData); i++ { 27 + mimc.Write(circuit.OperationData[i]) 28 + } 29 + 30 + computedSharedHash := mimc.Sum() 31 + api.AssertIsEqual(circuit.SharedHash, computedSharedHash) 32 + 33 + mimc.Reset() 34 + 35 + mimc.Write(circuit.ValidatorAddress) 36 + mimc.Write(computedSharedHash) 37 + 38 + api.AssertIsEqual(circuit.SpecificHash, mimc.Sum()) 39 + 40 + return nil 41 + }
+96
proof/block_challenge_test.go
··· 1 + package proof 2 + 3 + import ( 4 + "encoding/hex" 5 + "slices" 6 + "testing" 7 + "time" 8 + 9 + "github.com/consensys/gnark-crypto/ecc" 10 + "github.com/consensys/gnark-crypto/ecc/bn254" 11 + "github.com/consensys/gnark/backend/groth16" 12 + "github.com/consensys/gnark/frontend" 13 + "github.com/consensys/gnark/frontend/cs/r1cs" 14 + "github.com/samber/lo" 15 + "github.com/stretchr/testify/require" 16 + 17 + "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" 18 + ) 19 + 20 + func TestBlockChallenge(t *testing.T) { 21 + st := time.Now() 22 + 23 + var bcCircuit BlockChallengeCircuit 24 + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &bcCircuit) 25 + require.NoError(t, err) 26 + 27 + t.Log("Compile took", time.Since(st)) 28 + 29 + st = time.Now() 30 + pk, vk, err := groth16.Setup(r1cs) 31 + require.NoError(t, err) 32 + t.Log("Setup took", time.Since(st)) 33 + 34 + st = time.Now() 35 + origValidatorID := lo.Must(hex.DecodeString("deadbeefdeadbeefdeadbeefdeadbeef")) 36 + origLastCommitHash := lo.Must(hex.DecodeString("deadf00ddeadf00ddeadf00ddeadbeef")) 37 + 38 + opDataSlice := slices.Repeat(lo.Must(hex.DecodeString("ffee")), 775) 39 + opData := [1550]byte{} 40 + copy(opData[:], opDataSlice) 41 + assignmentShared, sharedHash := buildBlockChallengeCircuitAssignmentShared(origLastCommitHash, opData) 42 + assignment := buildBlockChallengeCircuitAssignmentFull(assignmentShared, sharedHash, origValidatorID) 43 + t.Log("Out of circuit took", time.Since(st)) 44 + 45 + st = time.Now() 46 + witness, err := frontend.NewWitness(assignment, bn254.ID.ScalarField()) 47 + require.NoError(t, err) 48 + t.Log("Witness took", time.Since(st)) 49 + 50 + st = time.Now() 51 + proof, err := groth16.Prove(r1cs, pk, witness) 52 + require.NoError(t, err) 53 + t.Log("Prove took", time.Since(st)) 54 + 55 + publicWitness, err := witness.Public() 56 + require.NoError(t, err) 57 + 58 + st = time.Now() 59 + err = groth16.Verify(proof, vk, publicWitness) 60 + require.NoError(t, err) 61 + t.Log("Verify took", time.Since(st)) 62 + } 63 + 64 + func buildBlockChallengeCircuitAssignmentShared(lastCommitHash []byte, data [1550]byte) (BlockChallengeCircuit, []byte) { 65 + var assignment BlockChallengeCircuit 66 + 67 + h := mimc.NewMiMC() 68 + 69 + if len(lastCommitHash) > 31 { 70 + lastCommitHash = lastCommitHash[len(lastCommitHash)-31:] 71 + } 72 + h.Write(lastCommitHash) 73 + assignment.LastCommitHash = lastCommitHash 74 + 75 + for i := 0; i < len(assignment.OperationData); i++ { 76 + d := data[31*i : 31*(i+1)] 77 + assignment.OperationData[i] = d 78 + h.Write(d) 79 + } 80 + 81 + sharedHash := h.Sum(nil) 82 + assignment.SharedHash = sharedHash 83 + return assignment, sharedHash 84 + } 85 + 86 + func buildBlockChallengeCircuitAssignmentFull(shared BlockChallengeCircuit, sharedHash, validatorAddress []byte) *BlockChallengeCircuit { 87 + h := mimc.NewMiMC() 88 + 89 + h.Write(validatorAddress) 90 + h.Write(sharedHash) 91 + 92 + shared.ValidatorAddress = validatorAddress 93 + shared.SpecificHash = h.Sum(nil) 94 + 95 + return &shared 96 + }
+62
proof/writer/writer.go
··· 1 + package main 2 + 3 + import ( 4 + "io" 5 + "log" 6 + "os" 7 + 8 + "github.com/consensys/gnark-crypto/ecc" 9 + "github.com/consensys/gnark/backend/groth16" 10 + "github.com/consensys/gnark/frontend" 11 + "github.com/consensys/gnark/frontend/cs/r1cs" 12 + "github.com/palantir/stacktrace" 13 + "tangled.org/gbl08ma.com/didplcbft/proof" 14 + ) 15 + 16 + func main() { 17 + var bcCircuit proof.BlockChallengeCircuit 18 + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &bcCircuit) 19 + if err != nil { 20 + log.Fatalln(stacktrace.Propagate(err, "failed to compile circuit")) 21 + } 22 + 23 + pk, vk, err := groth16.Setup(r1cs) 24 + if err != nil { 25 + log.Fatalln(stacktrace.Propagate(err, "failed setup")) 26 + } 27 + 28 + toWrite := []struct { 29 + writerTo io.WriterTo 30 + fileName string 31 + }{ 32 + { 33 + writerTo: r1cs, 34 + fileName: "../../abciapp/proofcircuit/BlockChallenge_ConstraintSystem", 35 + }, 36 + { 37 + writerTo: pk, 38 + fileName: "../../abciapp/proofcircuit/BlockChallenge_ProvingKey", 39 + }, 40 + { 41 + writerTo: vk, 42 + fileName: "../../abciapp/proofcircuit/BlockChallenge_VerifyingKey", 43 + }, 44 + } 45 + 46 + for _, entry := range toWrite { 47 + func() { 48 + file, err := os.OpenFile(entry.fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 49 + if err != nil { 50 + log.Fatalln(stacktrace.Propagate(err, "failed to open file %s for writing", entry.fileName)) 51 + } 52 + defer file.Close() 53 + 54 + _, err = entry.writerTo.WriteTo(file) 55 + if err != nil { 56 + log.Fatalln(stacktrace.Propagate(err, "failed write to file %s", entry.fileName)) 57 + } 58 + }() 59 + } 60 + 61 + log.Println("Done") 62 + }
+74 -14
store/consensus.go
··· 32 32 AuditLogReverseIterator(ctx context.Context, tx transaction.Read, did string, err *error) iter.Seq[types.SequencedLogEntry] 33 33 34 34 ExportOperations(ctx context.Context, tx transaction.Read, after uint64, count int) ([]types.SequencedLogEntry, error) // passing a count of zero means unlimited 35 - OperationsIterator(tx transaction.Read, after uint64, retErr *error) iter.Seq[types.SequencedLogEntry] 35 + OperationsIterator(tx transaction.Read, after uint64, retErr *error) iter.Seq2[types.SequencedLogEntry, []byte] 36 36 37 37 StoreOperation(ctx context.Context, tx transaction.Write, entry didplc.LogEntry, nullifyWithSequenceEqualOrGreaterThan mo.Option[uint64]) error 38 38 SetOperationCreatedAt(tx transaction.Write, seqID uint64, createdAt time.Time) error 39 39 40 - NextOperationSequence(tx transaction.Read) (uint64, error) 40 + CountOperations(tx transaction.Read) (uint64, error) 41 41 42 42 AuthoritativePLC(tx transaction.Read) (string, error) 43 43 SetAuthoritativePLC(tx transaction.Write, url string) error 44 44 45 45 AuthoritativeImportProgress(tx transaction.Read) (uint64, error) 46 46 SetAuthoritativeImportProgress(tx transaction.Write, nextCursor uint64) error 47 + 48 + BlockChallengeProof(tx transaction.Read, height uint64) ([]byte, error) 49 + BlockChalengeProofsIterator(tx transaction.Read, afterHeight uint64, retErr *error) iter.Seq2[uint64, []byte] // afterHeight is exclusive for consistency with OperationsIterator 50 + StoreBlockChallengeProof(ctx context.Context, tx transaction.WriteIndex, blockHeight uint64, proof []byte) error 47 51 } 48 52 49 53 var _ ConsensusStore = (*consensusStore)(nil) ··· 123 127 124 128 func (t *consensusStore) AuditLogReverseIterator(ctx context.Context, tx transaction.Read, did string, retErr *error) iter.Seq[types.SequencedLogEntry] { 125 129 return func(yield func(types.SequencedLogEntry) bool) { 130 + *retErr = nil 131 + 126 132 didBytes, err := DIDToBytes(did) 127 133 if err != nil { 128 134 *retErr = stacktrace.Propagate(err, "") ··· 195 201 func (t *consensusStore) ExportOperations(ctx context.Context, tx transaction.Read, after uint64, count int) ([]types.SequencedLogEntry, error) { 196 202 entries := make([]types.SequencedLogEntry, 0, count) 197 203 var iterErr error 198 - for logEntry := range t.OperationsIterator(tx, after, &iterErr) { 204 + for logEntry, _ := range t.OperationsIterator(tx, after, &iterErr) { 199 205 entries = append(entries, logEntry) 200 206 201 207 // this condition being checked here also makes it so that a count of zero means unlimited ··· 209 215 return entries, nil 210 216 } 211 217 212 - func (t *consensusStore) OperationsIterator(tx transaction.Read, after uint64, retErr *error) iter.Seq[types.SequencedLogEntry] { 213 - return func(yield func(types.SequencedLogEntry) bool) { 218 + func (t *consensusStore) OperationsIterator(tx transaction.Read, after uint64, retErr *error) iter.Seq2[types.SequencedLogEntry, []byte] { 219 + return func(yield func(types.SequencedLogEntry, []byte) bool) { 220 + *retErr = nil 221 + 214 222 // as the name suggests, after is an exclusive lower bound, but our iterators use inclusive lower bounds 215 223 start := after + 1 216 224 startKey := marshalOperationKey(start) ··· 231 239 return 232 240 } 233 241 234 - if !yield(logEntry) { 242 + if !yield(logEntry, opIterator.Value()) { 235 243 return 236 244 } 237 245 ··· 246 254 247 255 // StoreOperation stores an operation in the tree, nullifying existing operations whose index within the DID's history 248 256 // is lower or equal to the specified optional integer. 249 - // 250 - // Even though this function is not meant to overwrite operations (it will error) we ask the caller to provide the sequence 251 - // The caller is responsible for managing the sequence and invalidating it when needed (e.g. after a rollback) using 252 - // [[consensusStore.NextOperationSequence]]. 253 - // Pushing the responsibility to the caller is preferable in terms of performance, even if it leads to less safe code, 254 - // because getting the sequence from the tree within this function every time has a significant performance hit 255 257 func (t *consensusStore) StoreOperation(ctx context.Context, tx transaction.Write, entry didplc.LogEntry, nullifyWithSequenceEqualOrGreaterThan mo.Option[uint64]) error { 256 258 didBytes, err := DIDToBytes(entry.DID) 257 259 if err != nil { ··· 382 384 var minOperationKey = marshalOperationKey(0) 383 385 var maxOperationKey = marshalOperationKey(math.MaxInt64) 384 386 385 - func (t *consensusStore) NextOperationSequence(tx transaction.Read) (uint64, error) { 387 + func (t *consensusStore) CountOperations(tx transaction.Read) (uint64, error) { 386 388 seq := uint64(0) 387 389 388 390 itr, err := tx.Tree().Iterator(minOperationKey, maxOperationKey, false) ··· 398 400 return 0, stacktrace.Propagate(err, "") 399 401 } 400 402 } 401 - return seq + 1, stacktrace.Propagate(err, "") 403 + return seq, stacktrace.Propagate(err, "") 402 404 } 403 405 404 406 func DIDToBytes(did string) ([]byte, error) { ··· 619 621 _, err := tx.Tree().Set([]byte("aImportProgress"), value) 620 622 return stacktrace.Propagate(err, "") 621 623 } 624 + 625 + func marshalBlockChallengeProofKey(block uint64) []byte { 626 + key := make([]byte, 1+8) 627 + key[0] = 'c' 628 + 629 + binary.BigEndian.PutUint64(key[1:], block) 630 + 631 + return key 632 + } 633 + 634 + func unmarshalBlockChallengeProofKey(key []byte) uint64 { 635 + return binary.BigEndian.Uint64(key[1:9]) 636 + } 637 + 638 + var maxBlockChallengeProofKey = marshalBlockChallengeProofKey(math.MaxInt64) 639 + 640 + func (t *consensusStore) BlockChallengeProof(tx transaction.Read, height uint64) ([]byte, error) { 641 + key := marshalBlockChallengeProofKey(height) 642 + value, err := tx.IndexDB().Get(key) 643 + return value, stacktrace.Propagate(err, "") 644 + } 645 + 646 + func (t *consensusStore) BlockChalengeProofsIterator(tx transaction.Read, afterHeight uint64, retErr *error) iter.Seq2[uint64, []byte] { 647 + return func(yield func(uint64, []byte) bool) { 648 + *retErr = nil 649 + // as the name suggests, after is an exclusive lower bound, but our iterators use inclusive lower bounds 650 + start := afterHeight + 1 651 + startKey := marshalBlockChallengeProofKey(start) 652 + endKey := maxBlockChallengeProofKey 653 + 654 + proofsIterator, err := tx.IndexDB().Iterator(startKey, endKey) 655 + if err != nil { 656 + *retErr = stacktrace.Propagate(err, "") 657 + return 658 + } 659 + 660 + defer proofsIterator.Close() 661 + 662 + for proofsIterator.Valid() { 663 + blockHeight := unmarshalBlockChallengeProofKey(proofsIterator.Key()) 664 + 665 + if !yield(blockHeight, proofsIterator.Value()) { 666 + return 667 + } 668 + 669 + proofsIterator.Next() 670 + } 671 + err = proofsIterator.Error() 672 + if err != nil { 673 + *retErr = stacktrace.Propagate(err, "") 674 + } 675 + } 676 + } 677 + 678 + func (t *consensusStore) StoreBlockChallengeProof(ctx context.Context, tx transaction.WriteIndex, blockHeight uint64, proof []byte) error { 679 + err := tx.IndexDB().Set(marshalBlockChallengeProofKey(blockHeight), proof) 680 + return stacktrace.Propagate(err, "") 681 + }
+1 -1
testutil/testutil.go
··· 23 23 _, indexDB, err := badgertodbm.NewBadgerInMemoryDB() 24 24 require.NoError(t, err) 25 25 26 - factory, err := transaction.NewFactory(tree, indexDB, store.Consensus.NextOperationSequence, store.NewInMemoryDIDBloomFilterStore()) 26 + factory, err := transaction.NewFactory(tree, indexDB, store.Consensus.CountOperations, store.NewInMemoryDIDBloomFilterStore()) 27 27 require.NoError(t, err) 28 28 29 29 return factory, tree, indexDB
+38 -23
transaction/factory.go
··· 28 28 type Factory struct { 29 29 bloomFilterStorage BloomFilterStorage 30 30 31 - db ExtendedDB 32 - mutableTree *iavl.MutableTree 33 - sequenceGetter func(tx Read) (uint64, error) 31 + db ExtendedDB 32 + mutableTreeMu sync.Mutex 33 + mutableTree *iavl.MutableTree 34 + operationCounter func(tx Read) (uint64, error) 34 35 35 36 bloomMu sync.RWMutex 36 37 bloomSeq uint64 ··· 40 41 bloomLastSaveSeq uint64 41 42 } 42 43 43 - func NewFactory(tree *iavl.MutableTree, indexDB ExtendedDB, sequenceGetter func(tx Read) (uint64, error), bloomFilterStorage BloomFilterStorage) (*Factory, error) { 44 + func NewFactory(tree *iavl.MutableTree, indexDB ExtendedDB, operationCounter func(tx Read) (uint64, error), bloomFilterStorage BloomFilterStorage) (*Factory, error) { 44 45 f := &Factory{ 45 46 bloomFilterStorage: bloomFilterStorage, 46 47 db: indexDB, 47 48 mutableTree: tree, 48 - sequenceGetter: sequenceGetter, 49 + operationCounter: operationCounter, 49 50 } 50 51 51 52 var err error ··· 57 58 if err != nil { 58 59 return nil, stacktrace.Propagate(err, "") 59 60 } 60 - f.bloomSeq, err = f.sequenceGetter(tx) 61 + f.bloomSeq, err = f.operationCounter(tx) 61 62 // since the sequenceGetter always returns the next sequence 62 63 f.bloomSeq-- 63 64 return f, stacktrace.Propagate(err, "") 64 65 } 65 66 66 67 func (f *Factory) ReadWorking(ts time.Time) Read { 68 + f.mutableTreeMu.Lock() 69 + defer f.mutableTreeMu.Unlock() 70 + 67 71 return &readTx{ 68 - ts: ts, 69 - height: f.mutableTree.WorkingVersion(), 70 - mutableTree: f.mutableTree, 71 - db: f.db, 72 - sequenceGetter: f.sequenceGetter, 73 - bloomMu: &f.bloomMu, 74 - bloomFilter: f.bloomFilter, 75 - bloomSeq: &f.bloomSeq, 72 + ts: ts, 73 + height: f.mutableTree.WorkingVersion(), 74 + mutableTree: f.mutableTree, 75 + db: f.db, 76 + operationCounter: f.operationCounter, 77 + bloomMu: &f.bloomMu, 78 + bloomFilter: f.bloomFilter, 79 + bloomSeq: &f.bloomSeq, 76 80 } 77 81 } 78 82 79 83 func (f *Factory) ReadCommitted() Read { 80 - tx, err := f.ReadHeight(time.Now(), f.mutableTree.Version()) 84 + f.mutableTreeMu.Lock() 85 + defer f.mutableTreeMu.Unlock() 86 + 87 + tx, err := f.readHeightWithinMu(time.Now(), f.mutableTree.Version()) 81 88 if err != nil { 82 89 // this should never happen, it's not worth making the signature of this function more 83 90 // complex for an error we'll never return unless the ABCI application is yet to be initialized ··· 86 93 return tx 87 94 } 88 95 89 - func (f *Factory) ReadHeight(ts time.Time, height int64) (Read, error) { 96 + func (f *Factory) readHeightWithinMu(ts time.Time, height int64) (Read, error) { 90 97 immutable, err := f.mutableTree.GetImmutable(height) 91 98 if err != nil { 92 99 if !errors.Is(err, iavl.ErrVersionDoesNotExist) || height != 0 { ··· 104 111 } 105 112 } 106 113 return &readTx{ 107 - ts: ts, 108 - height: height, 109 - tree: AdaptImmutableTree(immutable), 110 - db: f.db, 111 - sequenceGetter: f.sequenceGetter, 112 - bloomMu: &f.bloomMu, 113 - bloomFilter: f.bloomFilter, 114 + ts: ts, 115 + height: height, 116 + tree: AdaptImmutableTree(immutable), 117 + db: f.db, 118 + operationCounter: f.operationCounter, 119 + bloomMu: &f.bloomMu, 120 + bloomFilter: f.bloomFilter, 114 121 }, nil 122 + } 123 + 124 + func (f *Factory) ReadHeight(ts time.Time, height int64) (Read, error) { 125 + f.mutableTreeMu.Lock() 126 + defer f.mutableTreeMu.Unlock() 127 + 128 + tx, err := f.readHeightWithinMu(ts, height) 129 + return tx, stacktrace.Propagate(err, "") 115 130 } 116 131 117 132 func (f *Factory) SaveDIDBloomFilter() error {
+14 -5
transaction/interface.go
··· 11 11 Height() int64 12 12 Timestamp() time.Time 13 13 14 + CountOperations() (uint64, error) 14 15 Tree() ReadTree 15 - IndexDB() ReadIndex 16 + IndexDB() IndexReader 16 17 TestDIDBloomFilter(did []byte) bool 17 18 18 19 Upgrade() (Write, error) 20 + UpgradeForIndexOnly() (WriteIndex, error) 19 21 } 20 22 21 23 type Write interface { ··· 24 26 25 27 NextSequence() (uint64, error) 26 28 Tree() UnifiedTree 27 - IndexDB() WriteIndex 29 + IndexDB() IndexWriter 28 30 TestDIDBloomFilter(did []byte) bool 29 31 // sequence is only used to track how up-to-date the bloom filter is. it is not added to the bloom filter 30 32 AddToDIDBloomFilter(did []byte, sequence uint64) ··· 35 37 Downgrade() Read 36 38 } 37 39 38 - type ReadIndex interface { 40 + type WriteIndex interface { 41 + IndexDB() IndexWriter 42 + 43 + Commit() error 44 + Rollback() error 45 + } 46 + 47 + type IndexReader interface { 39 48 Get([]byte) ([]byte, error) 40 49 Has(key []byte) (bool, error) 41 50 ··· 44 53 ReverseIterator(start, end []byte) (dbm.Iterator, error) 45 54 } 46 55 47 - type WriteIndex interface { 48 - ReadIndex 56 + type IndexWriter interface { 57 + IndexReader 49 58 50 59 Delete([]byte) error 51 60 Set([]byte, []byte) error
+23 -1
transaction/read_on_write_tx.go
··· 2 2 3 3 import ( 4 4 "time" 5 + 6 + "github.com/palantir/stacktrace" 5 7 ) 6 8 7 9 // readOnWriteTx is created from a write tx to allow a write tx to be passed to functions that accept a read-only transaction ··· 16 18 } 17 19 18 20 // IndexDB implements [Read]. 19 - func (d *readOnWriteTx) IndexDB() ReadIndex { 21 + func (d *readOnWriteTx) IndexDB() IndexReader { 20 22 // by returning the write index we get the read uncommitted behavior we want 21 23 d.w.createWriteIndexIfNeeded() 22 24 return d.w.writeIndex ··· 27 29 return d.w.readTx.ts 28 30 } 29 31 32 + // CountOperations implements [Read]. 33 + func (d *readOnWriteTx) CountOperations() (uint64, error) { 34 + if d.w.hasSeq { 35 + return d.w.seq - 1, nil 36 + } 37 + count, err := d.w.readTx.CountOperations() 38 + if err != nil { 39 + return 0, stacktrace.Propagate(err, "") 40 + } 41 + // since we did the work, might as well set it on the writeTx too 42 + d.w.seq = count 43 + d.w.hasSeq = true 44 + return count, stacktrace.Propagate(err, "") 45 + } 46 + 30 47 // Tree implements [Read]. 31 48 func (d *readOnWriteTx) Tree() ReadTree { 32 49 // we can return the mutable tree as-is because it already presents read unsaved behavior ··· 42 59 func (d *readOnWriteTx) Upgrade() (Write, error) { 43 60 return d.w, nil 44 61 } 62 + 63 + // UpgradeForIndexOnly implements [Read]. 64 + func (d *readOnWriteTx) UpgradeForIndexOnly() (WriteIndex, error) { 65 + return d.w, nil 66 + }
+15 -2
transaction/read_tx.go
··· 21 21 bloomFilter *bloom.BloomFilter 22 22 bloomSeq *uint64 23 23 24 - sequenceGetter func(tx Read) (uint64, error) 24 + operationCounter func(tx Read) (uint64, error) 25 25 } 26 26 27 27 // Height implements [Read]. ··· 34 34 return t.ts 35 35 } 36 36 37 + // CountOperations implements [Read]. 38 + func (t *readTx) CountOperations() (uint64, error) { 39 + count, err := t.operationCounter(t) 40 + return count, stacktrace.Propagate(err, "") 41 + } 42 + 37 43 // Tree implements [Read]. 38 44 func (t *readTx) Tree() ReadTree { 39 45 if t.mutableTree != nil { ··· 43 49 } 44 50 45 51 // IndexDB implements [Read]. 46 - func (t *readTx) IndexDB() ReadIndex { 52 + func (t *readTx) IndexDB() IndexReader { 47 53 return t.db 48 54 } 49 55 ··· 64 70 unsavedBloomAdditions: map[string]struct{}{}, 65 71 }, nil 66 72 } 73 + 74 + // UpgradeForIndexOnly implements [Read]. 75 + func (t *readTx) UpgradeForIndexOnly() (WriteIndex, error) { 76 + return &writeIndexTx{ 77 + db: t.db, 78 + }, nil 79 + }
+14 -14
transaction/write_index.go
··· 21 21 unsavedRemovals map[string]struct{} 22 22 } 23 23 24 - // Delete implements [WriteIndex]. 24 + // Delete implements [IndexWriter]. 25 25 func (w *writeIndex) Delete(key []byte) error { 26 26 err := w.batch.Delete(key) 27 27 if err != nil { ··· 35 35 return nil 36 36 } 37 37 38 - // Set implements [WriteIndex]. 38 + // Set implements [IndexWriter]. 39 39 func (w *writeIndex) Set(key []byte, value []byte) error { 40 40 err := w.batch.Set(key, value) 41 41 if err != nil { ··· 49 49 return nil 50 50 } 51 51 52 - // Get implements [WriteIndex]. 52 + // Get implements [IndexWriter]. 53 53 func (w *writeIndex) Get(key []byte) ([]byte, error) { 54 54 kstr := unsafeBytesToStr(key) 55 55 ··· 64 64 return v, stacktrace.Propagate(err, "") 65 65 } 66 66 67 - // Has implements [WriteIndex]. 67 + // Has implements [IndexWriter]. 68 68 func (w *writeIndex) Has(key []byte) (bool, error) { 69 69 kstr := unsafeBytesToStr(key) 70 70 ··· 79 79 return v, stacktrace.Propagate(err, "") 80 80 } 81 81 82 - // IteratorWithOptions implements [WriteIndex]. 82 + // IteratorWithOptions implements [IndexWriter]. 83 83 func (w *writeIndex) IteratorWithOptions(start []byte, end []byte, opts badger.IteratorOptions) (dbm.Iterator, error) { 84 84 v, err := newUnsavedIterator(start, end, !opts.Reverse, w.db, w.unsavedAdditions, w.unsavedRemovals, &opts) 85 85 return v, stacktrace.Propagate(err, "") 86 86 } 87 87 88 - // Iterator implements [WriteIndex]. 88 + // Iterator implements [IndexWriter]. 89 89 func (w *writeIndex) Iterator(start []byte, end []byte) (dbm.Iterator, error) { 90 90 v, err := newUnsavedIterator(start, end, true, w.db, w.unsavedAdditions, w.unsavedRemovals, nil) 91 91 return v, stacktrace.Propagate(err, "") 92 92 } 93 93 94 - // ReverseIterator implements [WriteIndex]. 94 + // ReverseIterator implements [IndexWriter]. 95 95 func (w *writeIndex) ReverseIterator(start []byte, end []byte) (dbm.Iterator, error) { 96 96 v, err := newUnsavedIterator(start, end, false, w.db, w.unsavedAdditions, w.unsavedRemovals, nil) 97 97 return v, stacktrace.Propagate(err, "") ··· 189 189 return iter, nil 190 190 } 191 191 192 - // Domain implements [[dbm.Iterator]]. 192 + // Domain implements [dbm.Iterator]. 193 193 func (iter *unsavedIterator) Domain() ([]byte, []byte) { 194 194 return iter.start, iter.end 195 195 } 196 196 197 - // Valid implements [[dbm.Iterator]]. 197 + // Valid implements [dbm.Iterator]. 198 198 func (iter *unsavedIterator) Valid() bool { 199 199 if iter.start != nil && iter.end != nil { 200 200 if bytes.Compare(iter.end, iter.start) != 1 { ··· 205 205 return iter.underlyingIterator.Valid() || iter.nextUnsavedNodeIdx < len(iter.unsavedKeysToSort) || (iter.nextKey != nil && iter.nextVal != nil) 206 206 } 207 207 208 - // Key implements [[dbm.Iterator]] 208 + // Key implements [dbm.Iterator] 209 209 func (iter *unsavedIterator) Key() []byte { 210 210 return iter.nextKey 211 211 } 212 212 213 - // Value implements [[dbm.Iterator]] 213 + // Value implements [dbm.Iterator] 214 214 func (iter *unsavedIterator) Value() []byte { 215 215 return iter.nextVal 216 216 } 217 217 218 - // Next implements [[dbm.Iterator]] 218 + // Next implements [dbm.Iterator] 219 219 // It's effectively running the constant space overhead algorithm for streaming through sorted lists: 220 220 // the sorted lists being underlying keys & unsavedKeyAdditions / unsavedKeyRemovals 221 221 func (iter *unsavedIterator) Next() { ··· 294 294 iter.nextVal = nil 295 295 } 296 296 297 - // Close implements [[dbm.Iterator]] 297 + // Close implements [dbm.Iterator] 298 298 func (iter *unsavedIterator) Close() error { 299 299 return stacktrace.Propagate(iter.underlyingIterator.Close(), "") 300 300 } 301 301 302 - // Error implements [[dbm.Iterator]] 302 + // Error implements [dbm.Iterator] 303 303 func (iter *unsavedIterator) Error() error { 304 304 return iter.err 305 305 }
+46
transaction/write_index_tx.go
··· 1 + package transaction 2 + 3 + import "github.com/palantir/stacktrace" 4 + 5 + type writeIndexTx struct { 6 + db ExtendedDB 7 + 8 + writeIndex *writeIndex 9 + } 10 + 11 + // Commit implements [WriteIndex]. 12 + func (w *writeIndexTx) Commit() error { 13 + if w.writeIndex != nil { 14 + err := w.writeIndex.Commit() 15 + if err != nil { 16 + return stacktrace.Propagate(err, "") 17 + } 18 + } 19 + 20 + return nil 21 + } 22 + 23 + // Rollback implements [WriteIndex]. 24 + func (w *writeIndexTx) Rollback() error { 25 + if w.writeIndex != nil { 26 + err := w.writeIndex.Rollback() 27 + if err != nil { 28 + return stacktrace.Propagate(err, "") 29 + } 30 + } 31 + return nil 32 + } 33 + 34 + // IndexDB implements [WriteIndex]. 35 + func (w *writeIndexTx) IndexDB() IndexWriter { 36 + if w.writeIndex == nil { 37 + w.writeIndex = &writeIndex{ 38 + batch: w.db.NewBatch(), 39 + db: w.db, 40 + unsavedAdditions: make(map[string][]byte), 41 + unsavedRemovals: make(map[string]struct{}), 42 + } 43 + } 44 + 45 + return w.writeIndex 46 + }
+3 -4
transaction/write_tx.go
··· 67 67 } 68 68 69 69 // IndexDB implements [Write]. 70 - func (w *writeTx) IndexDB() WriteIndex { 70 + func (w *writeTx) IndexDB() IndexWriter { 71 71 w.createWriteIndexIfNeeded() 72 72 73 73 return w.writeIndex ··· 82 82 func (w *writeTx) NextSequence() (uint64, error) { 83 83 if !w.hasSeq { 84 84 var err error 85 - w.seq, err = w.readTx.sequenceGetter(w.readTx) 85 + w.seq, err = w.readTx.operationCounter(w.readTx) 86 86 if err != nil { 87 87 return 0, stacktrace.Propagate(err, "") 88 88 } 89 - w.hasSeq = true 90 - return w.seq, nil 91 89 } 92 90 93 91 w.seq++ 92 + w.hasSeq = true 94 93 return w.seq, nil 95 94 } 96 95