Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions mocks/Application.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions node/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand All @@ -218,8 +218,8 @@ func TestFraudProofTrigger(t *testing.T) {
endCnt++
case "Commit":
commitCnt++
case "TriggerFraudProofGenerationMode":
triggerFraudProofGenerationModeCnt++
case "GenerateFraudProof":
generateFraudProofCnt++
}
}
aggregatorHeight := nodes[0].Store.Height()
Expand All @@ -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++ {
Expand Down Expand Up @@ -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()
Expand Down
35 changes: 23 additions & 12 deletions proto/tendermint/abci/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ message Request {
RequestApplySnapshotChunk apply_snapshot_chunk = 15;
RequestGetAppHash get_app_hash = 16;
RequestGenerateFraudProof generate_fraud_proof = 17;
RequestTriggerFraudProofGenerationMode trigger_fraud_proof_generation_mode = 18;
RequestVerifyFraudProof verify_fraud_proof = 18;
}
}

Expand Down Expand Up @@ -131,10 +131,17 @@ message RequestApplySnapshotChunk {
message RequestGetAppHash {}

// Generates a fraud proof
message RequestGenerateFraudProof {}
message RequestGenerateFraudProof {
RequestBeginBlock beginBlockRequest = 1 [(gogoproto.nullable) = false];
repeated RequestDeliverTx deliverTxRequests = 2;
RequestEndBlock endBlockRequest = 3;
}

// Triggers fraud proof generation mode
message RequestTriggerFraudProofGenerationMode {}
// Verifies a fraud proof
message RequestVerifyFraudProof {
FraudProof fraud_proof = 1;
bytes expected_app_hash = 2;
}

//----------------------------------------
// Response types
Expand All @@ -159,7 +166,7 @@ message Response {
ResponseApplySnapshotChunk apply_snapshot_chunk = 16;
ResponseGetAppHash get_app_hash = 17;
ResponseGenerateFraudProof generate_fraud_proof = 18;
ResponseTriggerFraudProofGenerationMode trigger_fraud_proof_generation_mode = 19;
ResponseVerifyFraudProof verify_fraud_proof = 19;
}
}

Expand Down Expand Up @@ -302,10 +309,10 @@ message ResponseGetAppHash {
}

message ResponseGenerateFraudProof {
FraudProof fraudProof = 1;
FraudProof fraud_proof = 1;
}

message ResponseTriggerFraudProofGenerationMode {
message ResponseVerifyFraudProof {
bool success = 1;
}

Expand Down Expand Up @@ -419,18 +426,22 @@ message Snapshot {
// Represents a single-round fraudProof
message FraudProof {
int64 block_height = 1;
bytes appHash = 2;
map<string, StateWitness> stateWitness = 3;
bytes app_hash = 2;
map<string, StateWitness> state_witness = 3;

RequestBeginBlock fraudulentBeginBlock = 4;
RequestDeliverTx fraudulentDeliverTx = 5;
RequestEndBlock fraudulentEndBlock = 6;
}

// 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;
bytes root_hash = 2;
// List of witness data
repeated WitnessData witnessData = 3;
repeated WitnessData witness_data = 3;
}

// Witness data containing a key/value pair and a SMT proof for said key/value pair
Expand Down Expand Up @@ -463,5 +474,5 @@ service ABCIApplication {
returns (ResponseApplySnapshotChunk);
rpc GetAppHash(RequestGetAppHash) returns (ResponseGetAppHash);
rpc GenerateFraudProof(RequestGenerateFraudProof) returns (ResponseGenerateFraudProof);
rpc TriggerFraudProofGenerationMode(RequestTriggerFraudProofGenerationMode) returns (ResponseTriggerFraudProofGenerationMode);
rpc VerifyFraudProof(RequestVerifyFraudProof) returns (ResponseVerifyFraudProof);
}
24 changes: 24 additions & 0 deletions proto/tendermint/version/types.proto
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 2 additions & 2 deletions rpc/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
156 changes: 104 additions & 52 deletions state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/celestiaorg/optimint/types"
)

var fraudProofsEnabled = true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This shouldn't be global. Please move it to BlockExecutor struct.


// BlockExecutor creates and applies blocks and maintains state.
type BlockExecutor struct {
proposerAddress []byte
Expand Down Expand Up @@ -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 {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

add an else here and halt there

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
Expand All @@ -301,80 +303,130 @@ 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,
})
if err != nil {
return nil, err
}
isr, err := e.getAppHash()
if err != nil {
return nil, err
beginBlockRequest := abci.RequestBeginBlock{
Hash: hash[:],
Header: abciHeader,
LastCommitInfo: abci.LastCommitInfo{
Round: 0,
Votes: nil,
},
ByzantineValidators: nil,
}
ISRs = append(ISRs, isr)
err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex)
abciResponses.BeginBlock, err = e.proxyApp.BeginBlockSync(beginBlockRequest)
if err != nil {
return nil, err
}
currentIsrIndex++

for _, tx := range block.Data.Txs {
res := e.proxyApp.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx})
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)
err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex)
if err != nil {
return nil, err
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++
}
deliverTxRequests := make([]*abci.RequestDeliverTx, len(block.Data.Txs))
for _, tx := range block.Data.Txs {
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())
}

abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(abci.RequestEndBlock{Height: int64(block.Header.Height)})
if err != nil {
return nil, err
}
isr, err = e.getAppHash()
if err != nil {
return nil, err
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++
}
}
ISRs = append(ISRs, isr)
err = e.checkFraudProofTrigger(isr, currentIsrs, currentIsrIndex)
endBlockRequest := abci.RequestEndBlock{Height: int64(block.Header.Height)}
abciResponses.EndBlock, err = e.proxyApp.EndBlockSync(endBlockRequest)
if err != nil {
return nil, err
}
if block.Data.IntermediateStateRoots.RawRootsList == nil {
// Block producer: Initial ISRs generated here
block.Data.IntermediateStateRoots.RawRootsList = ISRs

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, &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
block.Data.IntermediateStateRoots.RawRootsList = ISRs
}
}

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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Instead of passing [][]byte and index, you can pass element of currentIsrs directly.

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{})
if err != nil {
return err
}
return true
}
}
return nil
return false
}

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
}
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 {
Expand Down
Loading