Skip to content
This repository has been archived by the owner on Apr 30, 2024. It is now read-only.

feat: Fraud Proof generation and partial verification using IAVL store #1

Merged

Conversation

Manav-Aggarwal
Copy link
Member

@Manav-Aggarwal Manav-Aggarwal commented Oct 14, 2022

Description

Consolidates code across PRs from celestiaorg#245 along with replacing the usage of the SMT store with the IAVL store.

Covers Fraud Proof Generation and the first two levels of verification.

The third level of verification needs the functionality to set up a BaseApp from a Fraud Proof. This requires the ability to create IAVL Deep Subtree. This PR includes placeholders for these IAVL Deep Subtrees which can be added once rollkit/iavl#1 is wrapped up.


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title
  • added ! to the type prefix if API or client breaking change
  • targeted the correct branch (see PR Targeting)
  • provided a link to the relevant issue or specification
  • followed the guidelines for building modules
  • included the necessary unit and integration tests
  • added a changelog entry to CHANGELOG.md
  • included comments for documenting Go code
  • updated the relevant documentation or specification
  • reviewed "Files changed" and left comments if necessary
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed ! in the type prefix if API or client breaking change
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic
  • reviewed API design and naming
  • reviewed documentation is accurate
  • reviewed tests and test coverage
  • manually tested (if applicable)

@Manav-Aggarwal Manav-Aggarwal self-assigned this Oct 15, 2022
@Manav-Aggarwal Manav-Aggarwal added the fraudproofs State Fraud Proofs label Oct 15, 2022
@Manav-Aggarwal Manav-Aggarwal changed the title Fraud Proof prototype using IAVL store feat: Fraud Proof prototype using IAVL store Oct 16, 2022
@Manav-Aggarwal Manav-Aggarwal marked this pull request as ready for review October 16, 2022 14:11
@Manav-Aggarwal Manav-Aggarwal changed the title feat: Fraud Proof prototype using IAVL store feat: Create Fraud Proof prototype using IAVL store Oct 16, 2022
@Manav-Aggarwal Manav-Aggarwal changed the title feat: Create Fraud Proof prototype using IAVL store feat: Create Fraud Proof prototype using IAVL store: No Deep Subtree Oct 16, 2022
@Manav-Aggarwal Manav-Aggarwal changed the title feat: Create Fraud Proof prototype using IAVL store: No Deep Subtree feat: Fraud Proof generation and partial verification using IAVL store: No Deep Subtree Oct 16, 2022
@Manav-Aggarwal Manav-Aggarwal changed the title feat: Fraud Proof generation and partial verification using IAVL store: No Deep Subtree feat: Fraud Proof generation and partial verification using IAVL store Oct 16, 2022

appHash, err := cms.GetAppHash()
if err != nil {
panic(err)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is calling panic a standard pulled from the cosmos sdk?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for ABCI methods, if something goes wrong, there's no way to gracefully throw an error other than panic. If you look at the BeginBlock implementation, you'll see panic calls.

@Manav-Aggarwal Manav-Aggarwal linked an issue Oct 18, 2022 that may be closed by this pull request
Copy link
Member

@tzdybal tzdybal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💪

As discussed Yesterday, small issues should be addressed now, bigger ones can be extracted as followup-issues and implemented later.

baseapp/abci.go Show resolved Hide resolved
baseapp/abci.go Show resolved Hide resolved
baseapp/abci.go Show resolved Hide resolved
cms := app.cms.(*rootmulti.Store)
err := cms.LoadLastVersion()
if err != nil {
panic(err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any situation when this can happen? Because of pruning settings, genesis block?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happens when there is no last state to load form, added a inline comment mentioning it

baseapp/abci.go Outdated
}

func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFraudProof) {
nonFraudulentRequests := req.DeliverTxRequests[:len(req.DeliverTxRequests)-1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if EndBlock is fraudulent? Then all TXs are not fraudulent, but the last one will be skipped.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this case.

4. Generate a fraudproof which should ignore the uncommitted set of transactions, and export S1 into a fraudProof data structure (minimal snapshot)
5. Verify the fraudproof and check verification passes (done in light client)
6. Load a fresh baseapp, B2, with the contents of fraud proof data structure from (4) so it can begin from state S1.
7. Check if the SMT root hashes of the new app with saved SMT root hash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
7. Check if the SMT root hashes of the new app with saved SMT root hash
7. Check if the root hashes of the new app with saved root hash

6. Load a fresh baseapp, B2, with the contents of fraud proof data structure from (4) so it can begin from state S1.
7. Check if the SMT root hashes of the new app with saved SMT root hash

Tests to write in future:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👍 👍
I think we should create tracking issue for those tests and later implement them one by one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a tracking issue: #3

Tests can be child issues of this issue later.

}

// Returns true only if only one of the three pointers is nil
func (fraudProof *FraudProof) checkFraudulentStateTransition() bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add unit test covering all possible cases.

Copy link
Member Author

@Manav-Aggarwal Manav-Aggarwal Oct 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


func (fraudProof *FraudProof) verifyFraudProof() (bool, error) {
if !fraudProof.checkFraudulentStateTransition() {
return false, fmt.Errorf("fraudProof has more than one type of fradulent state transitions marked nil")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errors.New should be used instead of fmt.Errorf. This error should be extracted as package level sentinel error.

@@ -17,6 +20,10 @@ const (
iterValueOp operation = "iterValue"
)

var (
ErrBufferEmpty = fmt.Errorf("provided buffer is empty")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ErrBufferEmpty = fmt.Errorf("provided buffer is empty")
ErrBufferEmpty = errors.New("provided buffer is empty")

Comment on lines +59 to +61
for k := range fraudProof.stateWitness {
keys = append(keys, k)
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +102 to +139
for storeKey, stateWitness := range fraudProof.stateWitness {
proofOp := stateWitness.Proof
proof, err := types.CommitmentOpDecoder(proofOp)
if err != nil {
return false, err
}
if !bytes.Equal(proof.GetKey(), []byte(storeKey)) {
return false, fmt.Errorf("got storeKey: %s, expected: %s", string(proof.GetKey()), storeKey)
}
appHash, err := proof.Run([][]byte{stateWitness.RootHash})
if err != nil {
return false, err
}
if !bytes.Equal(appHash[0], fraudProof.appHash) {
return false, fmt.Errorf("got appHash: %s, expected: %s", string(fraudProof.appHash), string(fraudProof.appHash))
}
// Fraudproof verification on a substore level
for _, witness := range stateWitness.WitnessData {
proofOp, key, value := witness.Proof, witness.Key, witness.Value
if err != nil {
return false, err
}
if !bytes.Equal(key, proofOp.GetKey()) {
return false, fmt.Errorf("got key: %s, expected: %s for storeKey: %s", string(key), string(proof.GetKey()), storeKey)
}
proof, err := types.CommitmentOpDecoder(proofOp)
if err != nil {
return false, err
}
rootHash, err := proof.Run([][]byte{value})
if err != nil {
return false, err
}
if !bytes.Equal(rootHash[0], stateWitness.RootHash) {
return false, fmt.Errorf("got rootHash: %s, expected: %s for storeKey: %s", string(rootHash[0]), string(stateWitness.RootHash), storeKey)
}
}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +145 to +161
for storeKey, stateWitness := range fraudProof.stateWitness {
abciWitnessData := make([]*abci.WitnessData, 0, len(stateWitness.WitnessData))
for _, witnessData := range stateWitness.WitnessData {
abciWitness := abci.WitnessData{
Key: witnessData.Key,
Value: witnessData.Value,
Proof: &witnessData.Proof,
}
abciWitnessData = append(abciWitnessData, &abciWitness)
}
proof := stateWitness.Proof
abciStateWitness[storeKey] = &abci.StateWitness{
Proof: &proof,
RootHash: stateWitness.RootHash,
WitnessData: abciWitnessData,
}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +174 to +189
for storeKey, abciStateWitness := range abciFraudProof.StateWitness {
witnessData := make([]WitnessData, 0, len(abciStateWitness.WitnessData))
for _, abciWitnessData := range abciStateWitness.WitnessData {
witness := WitnessData{
Key: abciWitnessData.Key,
Value: abciWitnessData.Value,
Proof: *abciWitnessData.Proof,
}
witnessData = append(witnessData, witness)
}
stateWitness[storeKey] = StateWitness{
Proof: *abciStateWitness.Proof,
RootHash: abciStateWitness.RootHash,
WitnessData: witnessData,
}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +163 to +173
for _, sk := range s.keysByName {
storeKeys = append(storeKeys, sk)
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +399 to +414
for _, storeParams := range rs.storesParams {
if storeParams.traceWriter != nil {
buf, ok := storeParams.traceWriter.(*bytes.Buffer)
if ok {
buf.Reset()
}
}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +1057 to +1083
for key := range stores {
name := key.Name()
iavlStore, err := rs.GetIAVLStore(name)
if err != nil {
return nil, err
}
m[name], err = iavlStore.Root()
if err != nil {
return nil, err
}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Comment on lines +67 to +83
for storeKey, stateWitness := range fraudProof.stateWitness {
rootHash := stateWitness.RootHash
// TODO(manav): Replace with IAVL Deep Subtrees once implementation is done
substoreDeepSMT := smtlib.NewDeepSparseMerkleSubTree(smtlib.NewSimpleMap(), smtlib.NewSimpleMap(), sha256.New(), rootHash)
for _, witnessData := range stateWitness.WitnessData {
proofOp, key, val := witnessData.Proof, witnessData.Key, witnessData.Value
proof, err := types.CommitmentOpDecoder(proofOp)
if err != nil {
return nil, err
}
// TODO(manav): Replace with an IAVL proof instead of SMT here
smtProof := proof.(*smt.ProofOp).GetProof()
substoreDeepSMT.AddBranch(smtProof, key, val)
}
// TODO(manav): Replace with actual iavl stores
storeKeyToIAVLTree[storeKey] = &iavltree.MutableTree{}
}

Check warning

Code scanning / CodeQL

Iteration over map

Iteration over map may be a possible source of non-determinism
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
fraudproofs State Fraud Proofs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fraud Proof generation and partial verification using IAVL store
3 participants