From 656d4ee324380d0d58893c0d7306fa26053497c4 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 24 Aug 2022 10:21:42 -0400 Subject: [PATCH 1/6] Add skeleton to catch up and generate fraudproof after FPGM --- state/executor.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/state/executor.go b/state/executor.go index af9fd33f68..6efec66562 100644 --- a/state/executor.go +++ b/state/executor.go @@ -319,12 +319,16 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) + isFraud, err := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if err != nil { return nil, err } currentIsrIndex++ + if isFraud { + // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + } + for _, tx := range block.Data.Txs { res := e.proxyApp.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) if res.GetException() != nil { @@ -335,11 +339,14 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) + isFraud, err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if err != nil { return nil, err } currentIsrIndex++ + if isFraud { + // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + } } abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(abci.RequestEndBlock{Height: int64(block.Header.Height)}) @@ -351,10 +358,13 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) + isFraud, err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if err != nil { return nil, err } + if isFraud { + // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + } if block.Data.IntermediateStateRoots.RawRootsList == nil { // Block producer: Initial ISRs generated here block.Data.IntermediateStateRoots.RawRootsList = ISRs @@ -363,18 +373,24 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return abciResponses, nil } -func (e *BlockExecutor) checkFraudProofTrigger(generatedIsr []byte, currentIsrs [][]byte, index int) error { +func (e *BlockExecutor) checkFraudProofTrigger(generatedIsr []byte, currentIsrs [][]byte, index int) (bool, error) { if currentIsrs != nil { stateIsr := currentIsrs[index] if !bytes.Equal(stateIsr, generatedIsr) { e.logger.Debug("ISR Mismatch", "given_isr", stateIsr, "generated_isr", generatedIsr) - _, err := e.proxyApp.TriggerFraudProofGenerationModeSync(abci.RequestTriggerFraudProofGenerationMode{}) + resp, err := e.proxyApp.TriggerFraudProofGenerationModeSync(abci.RequestTriggerFraudProofGenerationMode{}) if err != nil { - return err + return false, err + } + if resp.Success { + return true, nil + + } else { + return false, fmt.Errorf("trigger fraud proof generation mode failed") } } } - return nil + return false, nil } func (e *BlockExecutor) getLastCommitHash(lastCommit *types.Commit, header *types.Header) []byte { From d749e4c3007062d5def5b397baefc1fca3bebad4 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 24 Aug 2022 10:35:06 -0400 Subject: [PATCH 2/6] Update TODOs --- state/executor.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/state/executor.go b/state/executor.go index 6efec66562..d704e7252f 100644 --- a/state/executor.go +++ b/state/executor.go @@ -326,7 +326,7 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t currentIsrIndex++ if isFraud { - // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + // TODO: call generate fraudproof, fraudTx: BeginBlock } for _, tx := range block.Data.Txs { @@ -345,7 +345,8 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t } currentIsrIndex++ if isFraud { - // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + // TODO: fast-forward app to right before this DeliverTx, call generate fraudproof, then gossip it to P2P network + // fraudTx: current DeliverTx } } @@ -363,7 +364,8 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } if isFraud { - // TODO: catch up app and call generate fraudproof, then gossip it to P2P network + // TODO: fast-forward app to right before EndBlock, call generate fraudproof, then gossip it to P2P network + // fraudTx: EndBlock } if block.Data.IntermediateStateRoots.RawRootsList == nil { // Block producer: Initial ISRs generated here From 3e7c1d99b73cbafd360d3ff733638d646c4d15d7 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 24 Aug 2022 15:52:26 -0400 Subject: [PATCH 3/6] Remove triggerFraudProofGenerationMode and refactor --- mocks/Application.go | 15 ----- node/integration_test.go | 10 ++-- proto/tendermint/abci/types.proto | 88 +++++----------------------- proto/tendermint/version/types.proto | 24 ++++++++ rpc/client/client_test.go | 4 +- state/executor.go | 67 +++++++++++---------- state/executor_test.go | 2 +- 7 files changed, 83 insertions(+), 127 deletions(-) create mode 100644 proto/tendermint/version/types.proto diff --git a/mocks/Application.go b/mocks/Application.go index 0f0b29049a..2164d6f162 100644 --- a/mocks/Application.go +++ b/mocks/Application.go @@ -96,21 +96,6 @@ func (_m *Application) GetAppHash(_a0 types.RequestGetAppHash) types.ResponseGet return r0 } -// Commit provides a mock function with given fields: -func (_m *Application) TriggerFraudProofGenerationMode(_a0 types.RequestTriggerFraudProofGenerationMode) types.ResponseTriggerFraudProofGenerationMode { - ret := _m.Called(_a0) - - var r0 types.ResponseTriggerFraudProofGenerationMode - if rf, ok := ret.Get(0).(func(types.RequestTriggerFraudProofGenerationMode) types.ResponseTriggerFraudProofGenerationMode); ok { - r0 = rf(_a0) - } else { - r0 = ret.Get(0).(types.ResponseTriggerFraudProofGenerationMode) - } - - return r0 -} - - // DeliverTx provides a mock function with given fields: _a0 func (_m *Application) DeliverTx(_a0 types.RequestDeliverTx) types.ResponseDeliverTx { ret := _m.Called(_a0) diff --git a/node/integration_test.go b/node/integration_test.go index 9b201c5d01..e46ecefe90 100644 --- a/node/integration_test.go +++ b/node/integration_test.go @@ -209,7 +209,7 @@ func TestFraudProofTrigger(t *testing.T) { beginCnt := 0 endCnt := 0 commitCnt := 0 - triggerFraudProofGenerationModeCnt := 0 + generateFraudProofCnt := 0 for _, call := range app.Calls { switch call.Method { case "BeginBlock": @@ -218,8 +218,8 @@ func TestFraudProofTrigger(t *testing.T) { endCnt++ case "Commit": commitCnt++ - case "TriggerFraudProofGenerationMode": - triggerFraudProofGenerationModeCnt++ + case "GenerateFraudProof": + generateFraudProofCnt++ } } aggregatorHeight := nodes[0].Store.Height() @@ -228,7 +228,7 @@ func TestFraudProofTrigger(t *testing.T) { assert.GreaterOrEqual(endCnt, adjustedHeight) assert.GreaterOrEqual(commitCnt, adjustedHeight) - assert.Equal(triggerFraudProofGenerationModeCnt, beginCnt+endCnt+clientNodes) + assert.Equal(generateFraudProofCnt, beginCnt+endCnt+clientNodes) // assert that all blocks known to node are same as produced by aggregator for h := uint64(1); h <= nodes[i].Store.Height(); h++ { @@ -300,7 +300,7 @@ func createNode(n int, isMalicious bool, aggregator bool, dalc da.DataAvailabili } if isMalicious && !aggregator { - app.On("TriggerFraudProofGenerationMode", mock.Anything).Return(abci.ResponseTriggerFraudProofGenerationMode{}) + app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{FraudProof: &abci.FraudProof{}}) } app.On("DeliverTx", mock.Anything).Return(abci.ResponseDeliverTx{}).Run(func(args mock.Arguments) { wg.Done() diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 0e02d0ad44..8e3a909363 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -36,9 +36,6 @@ message Request { RequestOfferSnapshot offer_snapshot = 13; RequestLoadSnapshotChunk load_snapshot_chunk = 14; RequestApplySnapshotChunk apply_snapshot_chunk = 15; - RequestGetAppHash get_app_hash = 16; - RequestGenerateFraudProof generate_fraud_proof = 17; - RequestTriggerFraudProofGenerationMode trigger_fraud_proof_generation_mode = 18; } } @@ -105,7 +102,8 @@ message RequestEndBlock { message RequestCommit {} // lists available snapshots -message RequestListSnapshots {} +message RequestListSnapshots { +} // offers a snapshot to the application message RequestOfferSnapshot { @@ -127,15 +125,6 @@ message RequestApplySnapshotChunk { string sender = 3; } -// Gets the current appHash -message RequestGetAppHash {} - -// Generates a fraud proof -message RequestGenerateFraudProof {} - -// Triggers fraud proof generation mode -message RequestTriggerFraudProofGenerationMode {} - //---------------------------------------- // Response types @@ -157,9 +146,6 @@ message Response { ResponseOfferSnapshot offer_snapshot = 14; ResponseLoadSnapshotChunk load_snapshot_chunk = 15; ResponseApplySnapshotChunk apply_snapshot_chunk = 16; - ResponseGetAppHash get_app_hash = 17; - ResponseGenerateFraudProof generate_fraud_proof = 18; - ResponseTriggerFraudProofGenerationMode trigger_fraud_proof_generation_mode = 19; } } @@ -226,12 +212,6 @@ message ResponseCheckTx { repeated Event events = 7 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; string codespace = 8; - string sender = 9; - int64 priority = 10; - - // mempool_error is set by Tendermint. - // ABCI applictions creating a ResponseCheckTX should not set mempool_error. - string mempool_error = 11; } message ResponseDeliverTx { @@ -241,17 +221,16 @@ message ResponseDeliverTx { string info = 4; // nondeterministic int64 gas_wanted = 5 [json_name = "gas_wanted"]; int64 gas_used = 6 [json_name = "gas_used"]; - repeated Event events = 7 [ - (gogoproto.nullable) = false, - (gogoproto.jsontag) = "events,omitempty" - ]; // nondeterministic + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic string codespace = 8; } message ResponseEndBlock { - repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false]; - ConsensusParams consensus_param_updates = 2; - repeated Event events = 3 + repeated ValidatorUpdate validator_updates = 1 + [(gogoproto.nullable) = false]; + ConsensusParams consensus_param_updates = 2; + repeated Event events = 3 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; } @@ -297,18 +276,6 @@ message ResponseApplySnapshotChunk { } } -message ResponseGetAppHash { - bytes app_hash = 1; -} - -message ResponseGenerateFraudProof { - FraudProof fraudProof = 1; -} - -message ResponseTriggerFraudProofGenerationMode { - bool success = 1; -} - //---------------------------------------- // Misc. @@ -397,8 +364,10 @@ message Evidence { // The height when the offense occurred int64 height = 3; // The corresponding time where the offense occurred - google.protobuf.Timestamp time = 4 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp time = 4 [ + (gogoproto.nullable) = false, + (gogoproto.stdtime) = true + ]; // Total voting power of the validator set in case the ABCI application does // not store historical validators. // https://github.com/tendermint/tendermint/issues/4581 @@ -416,30 +385,6 @@ message Snapshot { bytes metadata = 5; // Arbitrary application metadata } -// Represents a single-round fraudProof -message FraudProof { - int64 block_height = 1; - bytes appHash = 2; - map stateWitness = 3; -} - -// State witness with a list of all witness data -message StateWitness { - // store level proof - tendermint.crypto.ProofOp proof_op = 1; - // substore level hash - bytes rootHash = 2; - // List of witness data - repeated WitnessData witnessData = 3; -} - -// Witness data containing a key/value pair and a SMT proof for said key/value pair -message WitnessData { - bytes key = 1; - bytes value = 2; - tendermint.crypto.ProofOp proof = 3; -} - //---------------------------------------- // Service Definition @@ -457,11 +402,6 @@ service ABCIApplication { rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); - rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) - returns (ResponseLoadSnapshotChunk); - rpc ApplySnapshotChunk(RequestApplySnapshotChunk) - returns (ResponseApplySnapshotChunk); - rpc GetAppHash(RequestGetAppHash) returns (ResponseGetAppHash); - rpc GenerateFraudProof(RequestGenerateFraudProof) returns (ResponseGenerateFraudProof); - rpc TriggerFraudProofGenerationMode(RequestTriggerFraudProofGenerationMode) returns (ResponseTriggerFraudProofGenerationMode); + rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); + rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); } diff --git a/proto/tendermint/version/types.proto b/proto/tendermint/version/types.proto new file mode 100644 index 0000000000..6061868bd4 --- /dev/null +++ b/proto/tendermint/version/types.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; +package tendermint.version; + +option go_package = "github.com/tendermint/tendermint/proto/tendermint/version"; + +import "gogoproto/gogo.proto"; + +// App includes the protocol and software version for the application. +// This information is included in ResponseInfo. The App.Protocol can be +// updated in ResponseEndBlock. +message App { + uint64 protocol = 1; + string software = 2; +} + +// Consensus captures the consensus rules for processing a block in the blockchain, +// including all blockchain data structures and the rules of the application's +// state transition machine. +message Consensus { + option (gogoproto.equal) = true; + + uint64 block = 1; + uint64 app = 2; +} diff --git a/rpc/client/client_test.go b/rpc/client/client_test.go index 10fe3217b9..b9f371cc9c 100644 --- a/rpc/client/client_test.go +++ b/rpc/client/client_test.go @@ -421,7 +421,7 @@ func TestTx(t *testing.T) { mockApp.On("DeliverTx", mock.Anything).Return(abci.ResponseDeliverTx{}) mockApp.On("CheckTx", mock.Anything).Return(abci.ResponseCheckTx{}) mockApp.On("GetAppHash", mock.Anything).Return(abci.ResponseGetAppHash{}) - mockApp.On("TriggerFraudProofGenerationMode", mock.Anything).Return(abci.ResponseTriggerFraudProofGenerationMode{}) + mockApp.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{}) err = rpc.node.Start() require.NoError(err) @@ -636,7 +636,7 @@ func TestValidatorSetHandling(t *testing.T) { app.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{}) app.On("Commit", mock.Anything).Return(abci.ResponseCommit{}) app.On("GetAppHash", mock.Anything).Return(abci.ResponseGetAppHash{}) - app.On("TriggerFraudProofGenerationMode", mock.Anything).Return(abci.ResponseTriggerFraudProofGenerationMode{}) + app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{}) key, _, _ := crypto.GenerateEd25519Key(crand.Reader) signingKey, _, _ := crypto.GenerateEd25519Key(crand.Reader) diff --git a/state/executor.go b/state/executor.go index d704e7252f..463ea6bff4 100644 --- a/state/executor.go +++ b/state/executor.go @@ -319,15 +319,16 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - isFraud, err := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) - if err != nil { - return nil, err - } - currentIsrIndex++ - + isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - // TODO: call generate fraudproof, fraudTx: BeginBlock + fraudProof, err := e.generateFraudProof(block) + if err != nil { + return nil, err + } + // TODO: gossip fraudProof to P2P network + // fraudTx: BeginBlock } + currentIsrIndex++ for _, tx := range block.Data.Txs { res := e.proxyApp.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) @@ -339,15 +340,16 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - isFraud, err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) - if err != nil { - return nil, err - } - currentIsrIndex++ + isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - // TODO: fast-forward app to right before this DeliverTx, call generate fraudproof, then gossip it to P2P network + fraudProof, err := e.generateFraudProof(block) + if err != nil { + return nil, err + } + // TODO: gossip fraudProof to P2P network // fraudTx: current DeliverTx } + currentIsrIndex++ } abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(abci.RequestEndBlock{Height: int64(block.Header.Height)}) @@ -359,12 +361,13 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return nil, err } ISRs = append(ISRs, isr) - isFraud, err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) - if err != nil { - return nil, err - } + isFraud = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - // TODO: fast-forward app to right before EndBlock, call generate fraudproof, then gossip it to P2P network + fraudProof, err := e.generateFraudProof(block) + if err != nil { + return nil, err + } + // TODO: gossip fraudProof to P2P network // fraudTx: EndBlock } if block.Data.IntermediateStateRoots.RawRootsList == nil { @@ -375,24 +378,28 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t return abciResponses, nil } -func (e *BlockExecutor) checkFraudProofTrigger(generatedIsr []byte, currentIsrs [][]byte, index int) (bool, error) { +func (e *BlockExecutor) checkFraudProofTrigger(generatedIsr []byte, currentIsrs [][]byte, index int) bool { if currentIsrs != nil { stateIsr := currentIsrs[index] if !bytes.Equal(stateIsr, generatedIsr) { e.logger.Debug("ISR Mismatch", "given_isr", stateIsr, "generated_isr", generatedIsr) - resp, err := e.proxyApp.TriggerFraudProofGenerationModeSync(abci.RequestTriggerFraudProofGenerationMode{}) - if err != nil { - return false, err - } - if resp.Success { - return true, nil - - } else { - return false, fmt.Errorf("trigger fraud proof generation mode failed") - } + return true } } - return false, nil + return false +} + +func (e *BlockExecutor) generateFraudProof(block *types.Block) (*abci.FraudProof, error) { + resp, err := e.proxyApp.GenerateFraudProofSync(abci.RequestGenerateFraudProof{}) + if err != nil { + return nil, err + } + if resp.FraudProof != nil { + return resp.FraudProof, nil + + } else { + return nil, fmt.Errorf("fraud proof generation failed") + } } func (e *BlockExecutor) getLastCommitHash(lastCommit *types.Commit, header *types.Header) []byte { diff --git a/state/executor_test.go b/state/executor_test.go index 9b851f6d1d..b1a9db65b8 100644 --- a/state/executor_test.go +++ b/state/executor_test.go @@ -80,7 +80,7 @@ func TestApplyBlock(t *testing.T) { app.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{}) app.On("DeliverTx", mock.Anything).Return(abci.ResponseDeliverTx{}) app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{}) - app.On("TriggerFraudProofGenerationMode", mock.Anything).Return(abci.ResponseTriggerFraudProofGenerationMode{}) + app.On("GenerateFraudProof", mock.Anything).Return(abci.ResponseGenerateFraudProof{}) var mockAppHash [32]byte _, err := rand.Read(mockAppHash[:]) require.NoError(err) From af2e8cd7c93b7dc001ef5ec1208b57e3f20e759e Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 24 Aug 2022 16:43:53 -0400 Subject: [PATCH 4/6] Add generate fraud proof --- proto/tendermint/abci/types.proto | 82 +++++++++++++++++++++++++------ state/executor.go | 54 +++++++++++++------- 2 files changed, 103 insertions(+), 33 deletions(-) diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 8e3a909363..95a0bbbbff 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -36,6 +36,8 @@ message Request { RequestOfferSnapshot offer_snapshot = 13; RequestLoadSnapshotChunk load_snapshot_chunk = 14; RequestApplySnapshotChunk apply_snapshot_chunk = 15; + RequestGetAppHash get_app_hash = 16; + RequestGenerateFraudProof generate_fraud_proof = 17; } } @@ -102,8 +104,7 @@ message RequestEndBlock { message RequestCommit {} // lists available snapshots -message RequestListSnapshots { -} +message RequestListSnapshots {} // offers a snapshot to the application message RequestOfferSnapshot { @@ -125,6 +126,16 @@ message RequestApplySnapshotChunk { string sender = 3; } +// Gets the current appHash +message RequestGetAppHash {} + +// Generates a fraud proof +message RequestGenerateFraudProof { + RequestBeginBlock beginBlockRequest = 1 [(gogoproto.nullable) = false]; + repeated RequestDeliverTx deliverTxRequests = 2; + RequestEndBlock endBlockRequest = 3; +} + //---------------------------------------- // Response types @@ -146,6 +157,8 @@ message Response { ResponseOfferSnapshot offer_snapshot = 14; ResponseLoadSnapshotChunk load_snapshot_chunk = 15; ResponseApplySnapshotChunk apply_snapshot_chunk = 16; + ResponseGetAppHash get_app_hash = 17; + ResponseGenerateFraudProof generate_fraud_proof = 18; } } @@ -212,6 +225,12 @@ message ResponseCheckTx { repeated Event events = 7 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; string codespace = 8; + string sender = 9; + int64 priority = 10; + + // mempool_error is set by Tendermint. + // ABCI applictions creating a ResponseCheckTX should not set mempool_error. + string mempool_error = 11; } message ResponseDeliverTx { @@ -221,16 +240,17 @@ message ResponseDeliverTx { string info = 4; // nondeterministic int64 gas_wanted = 5 [json_name = "gas_wanted"]; int64 gas_used = 6 [json_name = "gas_used"]; - repeated Event events = 7 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic + repeated Event events = 7 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "events,omitempty" + ]; // nondeterministic string codespace = 8; } message ResponseEndBlock { - repeated ValidatorUpdate validator_updates = 1 - [(gogoproto.nullable) = false]; - ConsensusParams consensus_param_updates = 2; - repeated Event events = 3 + repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false]; + ConsensusParams consensus_param_updates = 2; + repeated Event events = 3 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; } @@ -276,6 +296,14 @@ message ResponseApplySnapshotChunk { } } +message ResponseGetAppHash { + bytes app_hash = 1; +} + +message ResponseGenerateFraudProof { + FraudProof fraud_proof = 1; +} + //---------------------------------------- // Misc. @@ -364,10 +392,8 @@ message Evidence { // The height when the offense occurred int64 height = 3; // The corresponding time where the offense occurred - google.protobuf.Timestamp time = 4 [ - (gogoproto.nullable) = false, - (gogoproto.stdtime) = true - ]; + google.protobuf.Timestamp time = 4 + [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; // Total voting power of the validator set in case the ABCI application does // not store historical validators. // https://github.com/tendermint/tendermint/issues/4581 @@ -385,6 +411,30 @@ message Snapshot { bytes metadata = 5; // Arbitrary application metadata } +// Represents a single-round fraudProof +message FraudProof { + int64 block_height = 1; + bytes app_hash = 2; + map state_witness = 3; +} + +// State witness with a list of all witness data +message StateWitness { + // store level proof + tendermint.crypto.ProofOp proof_op = 1; + // substore level hash + bytes root_hash = 2; + // List of witness data + repeated WitnessData witness_data = 3; +} + +// Witness data containing a key/value pair and a SMT proof for said key/value pair +message WitnessData { + bytes key = 1; + bytes value = 2; + tendermint.crypto.ProofOp proof = 3; +} + //---------------------------------------- // Service Definition @@ -402,6 +452,10 @@ service ABCIApplication { rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock); rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); - rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); - rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); + rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) + returns (ResponseLoadSnapshotChunk); + rpc ApplySnapshotChunk(RequestApplySnapshotChunk) + returns (ResponseApplySnapshotChunk); + rpc GetAppHash(RequestGetAppHash) returns (ResponseGetAppHash); + rpc GenerateFraudProof(RequestGenerateFraudProof) returns (ResponseGenerateFraudProof); } diff --git a/state/executor.go b/state/executor.go index 463ea6bff4..d132e413b8 100644 --- a/state/executor.go +++ b/state/executor.go @@ -301,16 +301,16 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t } abciHeader.ChainID = e.chainID abciHeader.ValidatorsHash = state.Validators.Hash() - abciResponses.BeginBlock, err = e.proxyApp.BeginBlockSync( - abci.RequestBeginBlock{ - Hash: hash[:], - Header: abciHeader, - LastCommitInfo: abci.LastCommitInfo{ - Round: 0, - Votes: nil, - }, - ByzantineValidators: nil, - }) + beginBlockRequest := abci.RequestBeginBlock{ + Hash: hash[:], + Header: abciHeader, + LastCommitInfo: abci.LastCommitInfo{ + Round: 0, + Votes: nil, + }, + ByzantineValidators: nil, + } + abciResponses.BeginBlock, err = e.proxyApp.BeginBlockSync(beginBlockRequest) if err != nil { return nil, err } @@ -321,17 +321,20 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t ISRs = append(ISRs, isr) isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - fraudProof, err := e.generateFraudProof(block) + fraudProof, err := e.generateFraudProof(&beginBlockRequest, nil, nil) if err != nil { return nil, err } // TODO: gossip fraudProof to P2P network // fraudTx: BeginBlock + _ = fraudProof } currentIsrIndex++ - + deliverTxRequests := make([]*abci.RequestDeliverTx, len(block.Data.Txs)) for _, tx := range block.Data.Txs { - res := e.proxyApp.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) + deliverTxRequest := abci.RequestDeliverTx{Tx: tx} + deliverTxRequests = append(deliverTxRequests, &deliverTxRequest) + res := e.proxyApp.DeliverTxAsync(deliverTxRequest) if res.GetException() != nil { return nil, errors.New(res.GetException().GetError()) } @@ -342,17 +345,18 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t ISRs = append(ISRs, isr) isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - fraudProof, err := e.generateFraudProof(block) + fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, nil) if err != nil { return nil, err } // TODO: gossip fraudProof to P2P network // fraudTx: current DeliverTx + _ = fraudProof } currentIsrIndex++ } - - abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(abci.RequestEndBlock{Height: int64(block.Header.Height)}) + endBlockRequest := abci.RequestEndBlock{Height: int64(block.Header.Height)} + abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(endBlockRequest) if err != nil { return nil, err } @@ -363,12 +367,13 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t ISRs = append(ISRs, isr) isFraud = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - fraudProof, err := e.generateFraudProof(block) + fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, &endBlockRequest) if err != nil { return nil, err } // TODO: gossip fraudProof to P2P network // fraudTx: EndBlock + _ = fraudProof } if block.Data.IntermediateStateRoots.RawRootsList == nil { // Block producer: Initial ISRs generated here @@ -389,8 +394,19 @@ func (e *BlockExecutor) checkFraudProofTrigger(generatedIsr []byte, currentIsrs return false } -func (e *BlockExecutor) generateFraudProof(block *types.Block) (*abci.FraudProof, error) { - resp, err := e.proxyApp.GenerateFraudProofSync(abci.RequestGenerateFraudProof{}) +func (e *BlockExecutor) generateFraudProof(beginBlockRequest *abci.RequestBeginBlock, deliverTxRequests []*abci.RequestDeliverTx, endBlockRequest *abci.RequestEndBlock) (*abci.FraudProof, error) { + generateFraudProofRequest := abci.RequestGenerateFraudProof{} + if beginBlockRequest == nil { + return nil, fmt.Errorf("begin block request cannot be a nil parameter") + } + generateFraudProofRequest.BeginBlockRequest = *beginBlockRequest + if deliverTxRequests != nil { + generateFraudProofRequest.DeliverTxRequests = deliverTxRequests + if endBlockRequest != nil { + generateFraudProofRequest.EndBlockRequest = endBlockRequest + } + } + resp, err := e.proxyApp.GenerateFraudProofSync(generateFraudProofRequest) if err != nil { return nil, err } From cf43b3bfa1bdee7da1304ecb282644114c91add4 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Wed, 24 Aug 2022 17:00:04 -0400 Subject: [PATCH 5/6] Wrap all fraudproof logic within a flag --- state/executor.go | 101 +++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/state/executor.go b/state/executor.go index d132e413b8..59f186b704 100644 --- a/state/executor.go +++ b/state/executor.go @@ -21,6 +21,8 @@ import ( "github.com/celestiaorg/optimint/types" ) +var fraudProofsEnabled = true + // BlockExecutor creates and applies blocks and maintains state. type BlockExecutor struct { proposerAddress []byte @@ -268,18 +270,18 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t currentIsrs := block.Data.IntermediateStateRoots.RawRootsList currentIsrIndex := 0 - if currentIsrs != nil { - expectedLength := len(block.Data.Txs) + 2 - // BeginBlock + DeliverTxs + EndBlock - if len(currentIsrs) != expectedLength { - return nil, fmt.Errorf("invalid length of ISR list: %d, expected length: %d", len(currentIsrs), expectedLength) + if fraudProofsEnabled { + if currentIsrs != nil { + expectedLength := len(block.Data.Txs) + 2 + // BeginBlock + DeliverTxs + EndBlock + if len(currentIsrs) != expectedLength { + return nil, fmt.Errorf("invalid length of ISR list: %d, expected length: %d", len(currentIsrs), expectedLength) + } } } ISRs := make([][]byte, 0) - var err error - e.proxyApp.SetResponseCallback(func(req *abci.Request, res *abci.Response) { if r, ok := res.Value.(*abci.Response_DeliverTx); ok { txRes := r.DeliverTx @@ -314,22 +316,25 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t if err != nil { return nil, err } - isr, err := e.getAppHash() - if err != nil { - return nil, err - } - ISRs = append(ISRs, isr) - isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) - if isFraud { - fraudProof, err := e.generateFraudProof(&beginBlockRequest, nil, nil) + + if fraudProofsEnabled { + isr, err := e.getAppHash() if err != nil { return nil, err } - // TODO: gossip fraudProof to P2P network - // fraudTx: BeginBlock - _ = fraudProof + ISRs = append(ISRs, isr) + isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) + if isFraud { + fraudProof, err := e.generateFraudProof(&beginBlockRequest, nil, nil) + if err != nil { + return nil, err + } + // TODO: gossip fraudProof to P2P network + // fraudTx: BeginBlock + _ = fraudProof + } + currentIsrIndex++ } - currentIsrIndex++ deliverTxRequests := make([]*abci.RequestDeliverTx, len(block.Data.Txs)) for _, tx := range block.Data.Txs { deliverTxRequest := abci.RequestDeliverTx{Tx: tx} @@ -338,6 +343,33 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t if res.GetException() != nil { return nil, errors.New(res.GetException().GetError()) } + + if fraudProofsEnabled { + isr, err := e.getAppHash() + if err != nil { + return nil, err + } + ISRs = append(ISRs, isr) + isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) + if isFraud { + fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, nil) + if err != nil { + return nil, err + } + // TODO: gossip fraudProof to P2P network + // fraudTx: current DeliverTx + _ = fraudProof + } + currentIsrIndex++ + } + } + endBlockRequest := abci.RequestEndBlock{Height: int64(block.Header.Height)} + abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(endBlockRequest) + if err != nil { + return nil, err + } + + if fraudProofsEnabled { isr, err := e.getAppHash() if err != nil { return nil, err @@ -345,39 +377,18 @@ func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *t ISRs = append(ISRs, isr) isFraud := e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) if isFraud { - fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, nil) + fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, &endBlockRequest) if err != nil { return nil, err } // TODO: gossip fraudProof to P2P network - // fraudTx: current DeliverTx + // fraudTx: EndBlock _ = fraudProof } - currentIsrIndex++ - } - endBlockRequest := abci.RequestEndBlock{Height: int64(block.Header.Height)} - abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(endBlockRequest) - if err != nil { - return nil, err - } - isr, err = e.getAppHash() - if err != nil { - return nil, err - } - ISRs = append(ISRs, isr) - isFraud = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex) - if isFraud { - fraudProof, err := e.generateFraudProof(&beginBlockRequest, deliverTxRequests, &endBlockRequest) - if err != nil { - return nil, err + if block.Data.IntermediateStateRoots.RawRootsList == nil { + // Block producer: Initial ISRs generated here + block.Data.IntermediateStateRoots.RawRootsList = ISRs } - // TODO: gossip fraudProof to P2P network - // fraudTx: EndBlock - _ = fraudProof - } - if block.Data.IntermediateStateRoots.RawRootsList == nil { - // Block producer: Initial ISRs generated here - block.Data.IntermediateStateRoots.RawRootsList = ISRs } return abciResponses, nil From 08ae55926196ffc7070fc5ae62664cf71ea4f924 Mon Sep 17 00:00:00 2001 From: Manav Aggarwal Date: Fri, 26 Aug 2022 18:39:52 -0400 Subject: [PATCH 6/6] Update types.proto --- proto/tendermint/abci/types.proto | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 95a0bbbbff..177fcd2cb2 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -38,6 +38,7 @@ message Request { RequestApplySnapshotChunk apply_snapshot_chunk = 15; RequestGetAppHash get_app_hash = 16; RequestGenerateFraudProof generate_fraud_proof = 17; + RequestVerifyFraudProof verify_fraud_proof = 18; } } @@ -136,6 +137,12 @@ message RequestGenerateFraudProof { RequestEndBlock endBlockRequest = 3; } +// Verifies a fraud proof +message RequestVerifyFraudProof { + FraudProof fraud_proof = 1; + bytes expected_app_hash = 2; +} + //---------------------------------------- // Response types @@ -159,6 +166,7 @@ message Response { ResponseApplySnapshotChunk apply_snapshot_chunk = 16; ResponseGetAppHash get_app_hash = 17; ResponseGenerateFraudProof generate_fraud_proof = 18; + ResponseVerifyFraudProof verify_fraud_proof = 19; } } @@ -304,6 +312,10 @@ message ResponseGenerateFraudProof { FraudProof fraud_proof = 1; } +message ResponseVerifyFraudProof { + bool success = 1; +} + //---------------------------------------- // Misc. @@ -416,6 +428,10 @@ message FraudProof { int64 block_height = 1; bytes app_hash = 2; map state_witness = 3; + + RequestBeginBlock fraudulentBeginBlock = 4; + RequestDeliverTx fraudulentDeliverTx = 5; + RequestEndBlock fraudulentEndBlock = 6; } // State witness with a list of all witness data @@ -458,4 +474,5 @@ service ABCIApplication { returns (ResponseApplySnapshotChunk); rpc GetAppHash(RequestGetAppHash) returns (ResponseGetAppHash); rpc GenerateFraudProof(RequestGenerateFraudProof) returns (ResponseGenerateFraudProof); + rpc VerifyFraudProof(RequestVerifyFraudProof) returns (ResponseVerifyFraudProof); }