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

Commit

Permalink
feat: Move tracing to IAVL tree substore for Fraud Proof implementati…
Browse files Browse the repository at this point in the history
…on (#8)

* New IAVL Design (#7)

* WIP: New IAVL design

* Fix typos and update docs

* Remove cosmos-sdk tracer usage

* Verify first existence proof

* Modify abci methods to accomodate new design

* refactor abci

* Baseapp test works now

* Remove usage of SetTracerFor

* Remove leftover code

* Update go mod file

* Run gofumpt

* Refactor and add docs

* Set length of witness
  • Loading branch information
Manav-Aggarwal authored Dec 2, 2022
1 parent e1448e7 commit ef454f0
Show file tree
Hide file tree
Showing 16 changed files with 220 additions and 299 deletions.
42 changes: 23 additions & 19 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,9 @@ func (app *BaseApp) executeNonFraudulentTransactions(req abci.RequestGenerateFra
}

// GenerateFraudProof implements the ABCI application interface. The BaseApp reverts to
// previous state, runs the given fraudulent state transition, and gets the trace representing
// the operations that this state transition makes. It then uses this trace to filter and export
// the pre-fraudulent state transition execution state of the BaseApp and generates a Fraud Proof
// previous state, runs the given fraudulent state transition, and gets the traced witness data representing
// the operations that this state transition makes. It then uses this traced witness data and
// the pre-fraudulent execution state of the BaseApp to generates a Fraud Proof
// representing it. It returns this generated Fraud Proof.
func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res abci.ResponseGenerateFraudProof) {
// Revert app to previous state
Expand All @@ -178,12 +178,15 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res
beginBlockRequest := req.BeginBlockRequest
isBeginBlockFraudulent := req.DeliverTxRequests == nil
isDeliverTxFraudulent := req.EndBlockRequest == nil
if isBeginBlockFraudulent {
cms.SetTracingEnabledAll(true)
}
app.BeginBlock(beginBlockRequest)
if !isBeginBlockFraudulent {
// BeginBlock is not the fraudulent state transition
app.executeNonFraudulentTransactions(req, isDeliverTxFraudulent)

cms.ResetAllTraceWriters()
cms.SetTracingEnabledAll(true)

// Record the trace made by the fraudulent state transitions
if isDeliverTxFraudulent {
Expand All @@ -196,15 +199,11 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res
}
}

// SubStore trace buffers now record the trace made by the fradulent state transition, so we copy them in new buffers
storeKeyToTraceBuf := make(map[string]*bytes.Buffer)
storeKeys := cms.GetStoreKeys()
for _, storeKey := range storeKeys {
subStoreTraceBuf := cms.GetTracerBufferFor(storeKey.Name())
traceBuf := bytes.Buffer{}
traceBuf.Write(subStoreTraceBuf.Bytes())
storeKeyToTraceBuf[storeKey.Name()] = &traceBuf
storeKeyToWitnessData, err := cms.GetWitnessDataMap()
if err != nil {
panic(err)
}

// Revert app to previous state
err = cms.LoadLastVersion()
if err != nil {
Expand All @@ -218,7 +217,7 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res
}

// Export the app's current trace-filtered state into a Fraud Proof and return it
fraudProof, err := app.getFraudProof(storeKeyToTraceBuf)
fraudProof, err := app.getFraudProof(storeKeyToWitnessData)
if err != nil {
panic(err)
}
Expand All @@ -231,9 +230,12 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res
default:
fraudProof.fraudulentEndBlock = req.EndBlockRequest
}
abciFraudProof := fraudProof.toABCI()
abciFraudProof, err := fraudProof.toABCI()
if err != nil {
panic(err)
}
res = abci.ResponseGenerateFraudProof{
FraudProof: &abciFraudProof,
FraudProof: abciFraudProof,
}
return res
}
Expand All @@ -245,16 +247,19 @@ func (app *BaseApp) GenerateFraudProof(req abci.RequestGenerateFraudProof) (res
func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci.ResponseVerifyFraudProof) {
abciFraudProof := req.FraudProof
fraudProof := FraudProof{}
fraudProof.fromABCI(*abciFraudProof)
err := fraudProof.fromABCI(*abciFraudProof)
if err != nil {
panic(err)
}

// First two levels of verification
// Store and subtore level verification
success, err := fraudProof.verifyFraudProof()
if err != nil {
panic(err)
}

if success {
// Third level of verification
// State execution verification
options := make([]func(*BaseApp), 0)
if app.routerOpts != nil {
for _, routerOpt := range app.routerOpts {
Expand Down Expand Up @@ -287,7 +292,6 @@ func (app *BaseApp) VerifyFraudProof(req abci.RequestVerifyFraudProof) (res abci
appFromFraudProof.BeginBlock(*fraudProof.fraudulentBeginBlock)
} else {
// Need to add some dummy begin block here since its a new app

appFromFraudProof.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: fraudProof.blockHeight}})
if fraudProof.fraudulentDeliverTx != nil {
resp := appFromFraudProof.DeliverTx(*fraudProof.fraudulentDeliverTx)
Expand Down
46 changes: 15 additions & 31 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package baseapp

import (
"bytes"
"fmt"
"strings"

Expand All @@ -17,9 +16,7 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/snapshots"
"github.com/cosmos/cosmos-sdk/store"
iavl "github.com/cosmos/cosmos-sdk/store/iavl"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
"github.com/cosmos/cosmos-sdk/store/tracekv"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -851,7 +848,7 @@ func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) {
}

// Generate a fraudproof for an app with the given trace buffers
func (app *BaseApp) getFraudProof(storeKeyToTraceBuf map[string]*bytes.Buffer) (FraudProof, error) {
func (app *BaseApp) getFraudProof(storeKeyToWitnessData map[string][]iavltree.WitnessData) (FraudProof, error) {
fraudProof := FraudProof{}
fraudProof.stateWitness = make(map[string]StateWitness)
fraudProof.blockHeight = app.LastBlockHeight()
Expand All @@ -862,10 +859,7 @@ func (app *BaseApp) getFraudProof(storeKeyToTraceBuf map[string]*bytes.Buffer) (
return FraudProof{}, err
}
fraudProof.appHash = appHash
nameToStoreKey := cms.StoreKeysByName()
for storeKeyName, traceBuf := range storeKeyToTraceBuf {
storeKey := nameToStoreKey[storeKeyName]
keys := cms.GetKVStore(storeKey).(*tracekv.Store).GetAllKeysUsedInTrace(*traceBuf)
for storeKeyName := range storeKeyToWitnessData {
iavlStore, err := cms.GetIAVLStore(storeKeyName)
if err != nil {
return FraudProof{}, err
Expand All @@ -881,40 +875,30 @@ func (app *BaseApp) getFraudProof(storeKeyToTraceBuf map[string]*bytes.Buffer) (
if err != nil {
return FraudProof{}, err
}
iavlWitnessData := storeKeyToWitnessData[storeKeyName]
stateWitness := StateWitness{
Proof: *proof,
RootHash: rootHash,
WitnessData: make([]WitnessData, 0),
WitnessData: make([]*WitnessData, 0, len(iavlWitnessData)),
}
populateStateWitness(&stateWitness, iavlStore, keys.Values())
populateStateWitness(&stateWitness, iavlWitnessData)
fraudProof.stateWitness[storeKeyName] = stateWitness
}

return fraudProof, nil
}

// populates the given state witness using traced keys and underlying iavl store
func populateStateWitness(stateWitness *StateWitness, iavlStore *iavl.Store, tracedKeys []string) {
for _, key := range tracedKeys {
bKey := []byte(key)
has := iavlStore.Has(bKey)
if has {
bVal := iavlStore.Get(bKey)
proof := iavlStore.GetProofFromTree(bKey)
witnessData := WitnessData{bKey, bVal, proof.GetOps()[0]}
stateWitness.WitnessData = append(stateWitness.WitnessData, witnessData)
} else {
dstProof := iavlStore.GetDSTNonExistenceProofFromDeepSubTree(bKey)
witnesses := iavlStore.DSTNonExistenceProofToWitnesses(dstProof)
for _, witness := range witnesses {
if witness != nil {
bKey := witness.Key
bVal := iavlStore.Get(bKey)
witnessData := WitnessData{bKey, bVal, *witness}
stateWitness.WitnessData = append(stateWitness.WitnessData, witnessData)
}
}
// populates the given state witness using the given witness data
func populateStateWitness(stateWitness *StateWitness, iavlWitnessData []iavltree.WitnessData) {
for _, iavlTraceOp := range iavlWitnessData {
proofOps := convertToProofOps(iavlTraceOp.Proofs)
witnessData := WitnessData{
Operation: iavlTraceOp.Operation,
Key: iavlTraceOp.Key,
Value: iavlTraceOp.Value,
Proofs: proofOps,
}
stateWitness.WitnessData = append(stateWitness.WitnessData, &witnessData)
}
}

Expand Down
20 changes: 4 additions & 16 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2312,10 +2312,6 @@ func TestGenerateAndLoadFraudProof(t *testing.T) {
4. Bad block, fraud proof needed, fraud proof works, chain halts (happy case)
*/

storeTraceBuf := &bytes.Buffer{}
subStoreTraceBuf1 := &bytes.Buffer{}
subStoreTraceBuf2 := &bytes.Buffer{}

routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
kv := msg.(*msgKeyValue)
Expand All @@ -2328,9 +2324,6 @@ func TestGenerateAndLoadFraudProof(t *testing.T) {
appB1 := setupBaseApp(t,
routerOpt,
)
appB1.SetCommitMultiStoreTracer(storeTraceBuf)
appB1.SetCommitKVStoreTracer(capKey1.Name(), subStoreTraceBuf1)
appB1.SetCommitKVStoreTracer(capKey2.Name(), subStoreTraceBuf2)

// B1 <- S0
appB1.InitChain(abci.RequestInitChain{})
Expand Down Expand Up @@ -2367,7 +2360,8 @@ func TestGenerateAndLoadFraudProof(t *testing.T) {

// Light Client
fraudProof := FraudProof{}
fraudProof.fromABCI(*resp.FraudProof)
err = fraudProof.fromABCI(*resp.FraudProof)
require.Nil(t, err)
require.Equal(t, appHashB1, fraudProof.appHash)
fraudProofVerified, err := fraudProof.verifyFraudProof()
require.Nil(t, err)
Expand All @@ -2390,10 +2384,6 @@ func TestGenerateAndLoadFraudProof(t *testing.T) {
}

func TestABCIEndToEndFraudProof(t *testing.T) {
storeTraceBuf := &bytes.Buffer{}
subStoreTraceBuf1 := &bytes.Buffer{}
subStoreTraceBuf2 := &bytes.Buffer{}

routerOpt := func(bapp *BaseApp) {
bapp.Router().AddRoute(sdk.NewRoute(routeMsgKeyValue, func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
kv := msg.(*msgKeyValue)
Expand All @@ -2402,13 +2392,10 @@ func TestABCIEndToEndFraudProof(t *testing.T) {
}))
}

// BaseApp, B1 with no Tracing
// BaseApp, B1
appB1 := setupBaseApp(t,
routerOpt,
)
appB1.SetCommitMultiStoreTracer(storeTraceBuf)
appB1.SetCommitKVStoreTracer(capKey1.Name(), subStoreTraceBuf1)
appB1.SetCommitKVStoreTracer(capKey2.Name(), subStoreTraceBuf2)

// B1 <- S0
appB1.InitChain(abci.RequestInitChain{})
Expand Down Expand Up @@ -2457,6 +2444,7 @@ func TestABCIEndToEndFraudProof(t *testing.T) {
}
routerOpts[capKey2.Name()] = newRouterOpt
appB1.routerOpts = routerOpts

// Light Client
verifyResp := appB1.VerifyFraudProof(
abci.RequestVerifyFraudProof{
Expand Down
Loading

0 comments on commit ef454f0

Please sign in to comment.