A very experimental PLC implementation which uses BFT consensus for decentralization
19
fork

Configure Feed

Select the types of activity you want to include in your feed.

Begin work on validator trust mechanism

gbl08ma c64bc7d9 a3fe0f8c

+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