Skip to content

Commit

Permalink
validating gossiped block is created by the proposer
Browse files Browse the repository at this point in the history
  • Loading branch information
srene committed Apr 29, 2024
1 parent 5e24df1 commit a5d9e88
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 12 deletions.
2 changes: 1 addition & 1 deletion node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func NewNode(
mpIDs := nodemempool.NewMempoolIDs()

// Set p2p client and it's validators
p2pValidator := p2p.NewValidator(logger.With("module", "p2p_validator"), pubsubServer)
p2pValidator := p2p.NewValidator(logger.With("module", "p2p_validator"), pubsubServer, settlementlc)

conf.P2P.GossipCacheSize = conf.BlockManagerConfig.GossipedBlocksCacheSize
conf.P2P.BoostrapTime = conf.BootstrapTime
Expand Down
12 changes: 11 additions & 1 deletion p2p/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/dymensionxyz/dymint/p2p/pb"
"github.com/dymensionxyz/dymint/types"
uevent "github.com/dymensionxyz/dymint/utils/event"
tmtypes "github.com/tendermint/tendermint/types"
)

/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -67,13 +68,22 @@ func (e *GossipedBlock) FromProto(other *pb.GossipedBlock) error {
}

// Validate run basic validation on the gossiped block
func (e *GossipedBlock) Validate() error {
func (e *GossipedBlock) Validate(proposer *types.Sequencer) error {
if err := e.Block.ValidateBasic(); err != nil {
return err
}
if err := e.Commit.ValidateBasic(); err != nil {
return err
}
if err := e.Commit.ValidateWithHeader(proposer, &e.Block.Header); err != nil {
return err
}
abciData := tmtypes.Data{
Txs: types.ToABCIBlockDataTxs(&e.Block.Data),
}
if e.Block.Header.DataHash != [32]byte(abciData.Hash()) {
return types.ErrInvalidHeaderDataHash
}
return nil
}

Expand Down
8 changes: 6 additions & 2 deletions p2p/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/dymensionxyz/dymint/mempool"
nodemempool "github.com/dymensionxyz/dymint/node/mempool"
"github.com/dymensionxyz/dymint/settlement"
"github.com/dymensionxyz/dymint/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/pubsub"
Expand All @@ -26,15 +27,17 @@ type IValidator interface {
type Validator struct {
logger types.Logger
localPubsubServer *pubsub.Server
slClient settlement.LayerI
}

var _ IValidator = (*Validator)(nil)

// NewValidator creates a new Validator.
func NewValidator(logger types.Logger, pusbsubServer *pubsub.Server) *Validator {
func NewValidator(logger types.Logger, pusbsubServer *pubsub.Server, slClient settlement.LayerI) *Validator {
return &Validator{
logger: logger,
localPubsubServer: pusbsubServer,
slClient: slClient,
}
}

Expand Down Expand Up @@ -78,10 +81,11 @@ func (v *Validator) BlockValidator() GossipValidator {
v.logger.Error("deserialize gossiped block", "error", err)
return false
}
if err := gossipedBlock.Validate(); err != nil {
if err := gossipedBlock.Validate(v.slClient.GetProposer()); err != nil {
v.logger.Error("Invalid gossiped block", "error", err)
return false
}

err := v.localPubsubServer.PublishWithEvents(context.Background(), gossipedBlock, map[string][]string{EventTypeKey: {EventNewGossipedBlock}})
if err != nil {
v.logger.Error("publishing event", "err", err)
Expand Down
109 changes: 106 additions & 3 deletions p2p/validator_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
package p2p_test

import (
"encoding/hex"
"testing"

"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
mempoolv1 "github.com/dymensionxyz/dymint/mempool/v1"
"github.com/dymensionxyz/dymint/types"

"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/pubsub"
"github.com/tendermint/tendermint/proxy"

"github.com/tendermint/tendermint/types"
cfg "github.com/tendermint/tendermint/config"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/dymensionxyz/dymint/block"
"github.com/dymensionxyz/dymint/mempool"
"github.com/dymensionxyz/dymint/mocks"
nodemempool "github.com/dymensionxyz/dymint/node/mempool"
"github.com/dymensionxyz/dymint/p2p"
"github.com/dymensionxyz/dymint/settlement"
"github.com/dymensionxyz/dymint/settlement/registry"
)

func TestValidator_TxValidator(t *testing.T) {
Expand Down Expand Up @@ -72,19 +85,109 @@ func TestValidator_TxValidator(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger := log.TestingLogger()
validateTx := p2p.NewValidator(logger, nil).TxValidator(tt.args.mp, nodemempool.NewMempoolIDs())
validateTx := p2p.NewValidator(logger, nil, nil).TxValidator(tt.args.mp, nodemempool.NewMempoolIDs())
valid := validateTx(txMsg)
assert.Equalf(t, tt.want, valid, "validateTx() = %v, want %v", valid, tt.want)
})
}
}

func TestValidator_BlockValidator(t *testing.T) {

//Create proposer for the block
proposerKey := ed25519.GenPrivKey()
//Create another key
attackerKey := ed25519.GenPrivKey()

tests := []struct {
name string
proposerKey *ed25519.PrivKey
valid bool
}{
{
name: "valid: block signed by proposer",
proposerKey: proposerKey,
valid: true,
}, {
name: "invalid: bad signer",
proposerKey: attackerKey,
valid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger := log.TestingLogger()

//Create Block executor
app := &mocks.Application{}
clientCreator := proxy.NewLocalClientCreator(app)
abciClient, err := clientCreator.NewABCIClient()
require.NoError(t, err)
require.NotNil(t, clientCreator)
require.NotNil(t, abciClient)
namespaceId := "0102030405060708"
mpool := mempoolv1.NewTxMempool(logger, cfg.DefaultMempoolConfig(), proxy.NewAppConnMempool(abciClient), 0)
executor, err := block.NewExecutor([]byte("test address"), namespaceId, "test", mpool, proxy.NewAppConns(clientCreator), nil, logger)
assert.NoError(t, err)

//Create state
state := types.State{}
state.ConsensusParams.Block.MaxBytes = 100
state.ConsensusParams.Block.MaxGas = 100000
state.Validators = tmtypes.NewValidatorSet(nil)

//Create empty block
block := executor.CreateBlock(1, &types.Commit{}, [32]byte{}, state)

//Create slclient
client := registry.GetClient(registry.Local)
pubsubServer := pubsub.NewServer()
err = pubsubServer.Start()
require.NoError(t, err)
err = client.Init(settlement.Config{ProposerPubKey: hex.EncodeToString(proposerKey.PubKey().Bytes())}, pubsubServer, log.TestingLogger())
require.NoError(t, err)

// Create commit for the block
abciHeaderPb := types.ToABCIHeaderPB(&block.Header)
abciHeaderBytes, err := abciHeaderPb.Marshal()
require.NoError(t, err)
var signature []byte
if tt.valid {
signature, err = proposerKey.Sign(abciHeaderBytes)
require.NoError(t, err)
} else {
signature, err = attackerKey.Sign(abciHeaderBytes)
require.NoError(t, err)
}
commit := &types.Commit{
Height: block.Header.Height,
HeaderHash: block.Header.Hash(),
Signatures: []types.Signature{signature},
}

//Create gossiped block
gossipedBlock := p2p.GossipedBlock{Block: *block, Commit: *commit}
gossipedBlockBytes, err := gossipedBlock.MarshalBinary()
require.NoError(t, err)
var blockMsg = &p2p.GossipMessage{
Data: gossipedBlockBytes,
From: peer.ID("from"),
}

//Check block validity
validateBlock := p2p.NewValidator(logger, pubsubServer, client).BlockValidator()
valid := validateBlock(blockMsg)
require.Equal(t, tt.valid, valid)
})
}
}

type mockMP struct {
mempool.Mempool
err error
}

func (m *mockMP) CheckTx(_ types.Tx, cb func(*abci.Response), _ mempool.TxInfo) error {
func (m *mockMP) CheckTx(_ tmtypes.Tx, cb func(*abci.Response), _ mempool.TxInfo) error {
if cb != nil {
code := abci.CodeTypeOK
if m.err != nil {
Expand Down
3 changes: 3 additions & 0 deletions settlement/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ func (b *BaseLayerClient) Init(config Config, pubsub *pubsub.Server, logger type
apply(b)
}

//TODO(srene): For a correct validation, sequencer list would need to be updated after a sequencer list change on the Hub.
//e.g. after receiving an event from the Hub. Right now, node will need to be restarted after a sequencer change, since it is
//only getting the sequencers list during Init.
b.sequencersList, err = b.fetchSequencersList()
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions settlement/dymension/dymension.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ func (d *HubClient) convertBatchToMsgUpdateState(batch *types.Batch, daResult *d
}
blockDescriptors[index] = blockDescriptor
}

settlementBatch := &rollapptypes.MsgUpdateState{
Creator: addr,
RollappId: d.config.RollappID,
Expand Down
2 changes: 1 addition & 1 deletion testutil/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func GetManagerWithProposerKey(conf config.BlockManagerConfig, proposerKey crypt
if err != nil {
return nil, err
}
p2pValidator := p2p.NewValidator(logger, pubsubServer)
p2pValidator := p2p.NewValidator(logger, pubsubServer, settlementlc)
p2pClient.SetTxValidator(p2pValidator.TxValidator(mp, mpIDs))
p2pClient.SetBlockValidator(p2pValidator.BlockValidator())

Expand Down
9 changes: 5 additions & 4 deletions types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package types
import "errors"

var (
ErrInvalidSignature = errors.New("invalid signature")
ErrNoStateFound = errors.New("no state found")
ErrSkippedEmptyBlock = errors.New("skipped empty block")
ErrInvalidBlockHeight = errors.New("invalid block height")
ErrInvalidSignature = errors.New("invalid signature")
ErrNoStateFound = errors.New("no state found")
ErrSkippedEmptyBlock = errors.New("skipped empty block")
ErrInvalidBlockHeight = errors.New("invalid block height")
ErrInvalidHeaderDataHash = errors.New("header not matching block data")
)

0 comments on commit a5d9e88

Please sign in to comment.