Skip to content

Commit

Permalink
Added commit validation to check for signature validity according to …
Browse files Browse the repository at this point in the history
…current sequencer. (#177)
  • Loading branch information
omritoptix authored Jan 10, 2023
1 parent 52cf587 commit 2fb8663
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 54 deletions.
9 changes: 6 additions & 3 deletions block/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,11 @@ func (m *Manager) applyBlock(ctx context.Context, block *types.Block, commit *ty
return err
}

// Currently we're assuming proposer is never nil as it's a pre-condition for
// dymint to start
proposer := m.settlementClient.GetProposer()
// Apply the block but DONT commit
newState, responses, err := m.executor.ApplyBlock(ctx, m.lastState, block)
newState, responses, err := m.executor.ApplyBlock(ctx, m.lastState, block, commit, proposer)
if err != nil {
m.logger.Error("failed to ApplyBlock", "error", err)
return err
Expand Down Expand Up @@ -534,11 +537,11 @@ func (m *Manager) produceBlock(ctx context.Context) error {
m.logger.Debug("block info", "num_tx", len(block.Data.Txs))

abciHeaderPb := abciconv.ToABCIHeaderPB(&block.Header)
headerBytes, err := abciHeaderPb.Marshal()
abciHeaderBytes, err := abciHeaderPb.Marshal()
if err != nil {
return err
}
sign, err := m.proposerKey.Sign(headerBytes)
sign, err := m.proposerKey.Sign(abciHeaderBytes)
if err != nil {
return err
}
Expand Down
58 changes: 42 additions & 16 deletions block/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const batchNotFoundErrorMessage = "batch not found"

func TestInitialState(t *testing.T) {
assert := assert.New(t)

genesis := testutil.GenerateGenesis(123)
sampleState := testutil.GenerateState(1, 128)
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
Expand Down Expand Up @@ -142,7 +141,8 @@ func TestWaitUntilSynced(t *testing.T) {
t.Log("Taking the manager out of sync by submitting a batch")
startHeight := atomic.LoadUint64(&manager.syncTarget) + 1
endHeight := startHeight + uint64(defaultBatchSize-1)*2
batch := testutil.GenerateBatch(startHeight, endHeight)
batch, err := testutil.GenerateBatch(startHeight, endHeight, manager.proposerKey)
require.NoError(t, err)
daResult := &da.ResultSubmitBatch{
BaseResult: da.BaseResult{
DAHeight: 1,
Expand Down Expand Up @@ -179,7 +179,8 @@ func TestPublishAfterSynced(t *testing.T) {
nextBatchStartHeight := atomic.LoadUint64(&manager.syncTarget) + 1
var batch *types.Batch
for i := 0; i < numBatchesToAdd; i++ {
batch = testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1))
batch, err = testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1), manager.proposerKey)
assert.NoError(t, err)
daResultSubmitBatch := manager.dalc.SubmitBatch(batch)
assert.Equal(t, daResultSubmitBatch.Code, da.StatusSuccess)
resultSubmitBatch := manager.settlementClient.SubmitBatch(batch, manager.dalc.GetClientType(), &daResultSubmitBatch)
Expand All @@ -195,19 +196,19 @@ func TestPublishAfterSynced(t *testing.T) {
go manager.ProduceBlockLoop(ctx)
select {
case <-ctx.Done():
assert.Equal(t, manager.store.Height(), lastStoreHeight)
assert.Equal(t, lastStoreHeight, manager.store.Height())
}

// Sync the manager
ctx, cancel = context.WithTimeout(context.Background(), time.Second*5)
ctx, cancel = context.WithTimeout(context.Background(), time.Second*2)
defer cancel()
go manager.SyncTargetLoop(ctx)
go manager.RetriveLoop(ctx)
go manager.ApplyBlockLoop(ctx)
select {
case <-ctx.Done():
assert.Greater(t, manager.store.Height(), lastStoreHeight)
assert.Equal(t, manager.store.Height(), batch.EndHeight)
assert.Equal(t, batch.EndHeight, manager.store.Height())
}

// Validate blocks are produced
Expand All @@ -227,7 +228,8 @@ func TestPublishWhenSettlementLayerDisconnected(t *testing.T) {
require.NotNil(t, manager)

nextBatchStartHeight := atomic.LoadUint64(&manager.syncTarget) + 1
var batch = testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1))
batch, err := testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1), manager.proposerKey)
assert.NoError(t, err)
daResultSubmitBatch := manager.dalc.SubmitBatch(batch)
assert.Equal(t, daResultSubmitBatch.Code, da.StatusSuccess)
resultSubmitBatch := manager.settlementClient.SubmitBatch(batch, manager.dalc.GetClientType(), &daResultSubmitBatch)
Expand All @@ -247,7 +249,8 @@ func TestPublishWhenDALayerDisconnected(t *testing.T) {
require.NotNil(t, manager)

nextBatchStartHeight := atomic.LoadUint64(&manager.syncTarget) + 1
var batch = testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1))
batch, err := testutil.GenerateBatch(nextBatchStartHeight, nextBatchStartHeight+uint64(defaultBatchSize-1), manager.proposerKey)
assert.NoError(t, err)
daResultSubmitBatch := manager.dalc.SubmitBatch(batch)
assert.Equal(t, daResultSubmitBatch.Code, da.StatusError)

Expand Down Expand Up @@ -311,7 +314,9 @@ func TestProducePendingBlock(t *testing.T) {
manager, err := getManager(nil, nil, 1, 1, 0, proxyApp)
require.NoError(t, err)
// Generate block and commit and save it to the store
block := testutil.GenerateBlocks(1, 1)[0]
blocks, err := testutil.GenerateBlocks(1, 1, manager.proposerKey)
require.NoError(t, err)
block := blocks[0]
_, err = manager.store.SaveBlock(block, &block.LastCommit, nil)
require.NoError(t, err)
// Produce block
Expand Down Expand Up @@ -364,16 +369,28 @@ func getManager(settlementlc settlement.LayerClient, dalc da.DataAvailabilityLay
if _, err := store.UpdateState(state, nil); err != nil {
return nil, err
}
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
conf := getManagerConfig()
logger := log.TestingLogger()
pubsubServer := pubsub.NewServer()
pubsubServer.Start()

// Init the settlement layer mock
if settlementlc == nil {
settlementlc = slregistry.GetClient(slregistry.Mock)
}
_ = initSettlementLayerMock(settlementlc, defaultBatchSize, uint64(state.LastBlockHeight), uint64(state.LastBlockHeight)+1, pubsubServer, logger)
//TODO(omritoptix): Change the initialization. a bit dirty.
proposerKey, proposerPubKey, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return nil, err
}
pubKeybytes, err := proposerPubKey.Raw()
if err != nil {
return nil, err
}
err = initSettlementLayerMock(settlementlc, defaultBatchSize, pubKeybytes, pubsubServer, logger)
if err != nil {
return nil, err
}

if dalc == nil {
dalc = &mockda.DataAvailabilityLayerClient{}
Expand All @@ -394,8 +411,8 @@ func getManager(settlementlc settlement.LayerClient, dalc da.DataAvailabilityLay
mpIDs := nodemempool.NewMempoolIDs()

// Init p2p client and validator
privKey, _, _ := crypto.GenerateEd25519Key(rand.Reader)
p2pClient, err := p2p.NewClient(config.P2PConfig{}, privKey, "TestChain", logger)
p2pKey, _, _ := crypto.GenerateEd25519Key(rand.Reader)
p2pClient, err := p2p.NewClient(config.P2PConfig{}, p2pKey, "TestChain", logger)
if err != nil {
return nil, err
}
Expand All @@ -407,7 +424,7 @@ func getManager(settlementlc settlement.LayerClient, dalc da.DataAvailabilityLay
return nil, err
}

manager, err := NewManager(key, conf, genesis, store, mp, proxyApp, dalc, settlementlc, nil, pubsubServer, p2pClient, logger)
manager, err := NewManager(proposerKey, conf, genesis, store, mp, proxyApp, dalc, settlementlc, nil, pubsubServer, p2pClient, logger)
if err != nil {
return nil, err
}
Expand All @@ -428,14 +445,23 @@ func initDALCMock(dalc da.DataAvailabilityLayerClient, daBlockTime time.Duration
}

// TODO(omritoptix): Possible move out to a generic testutil
func initSettlementLayerMock(settlementlc settlement.LayerClient, batchSize uint64, latestHeight uint64, batchOffsetHeight uint64, pubsubServer *pubsub.Server, logger log.Logger) error {
func initSettlementLayerMock(settlementlc settlement.LayerClient, batchSize uint64, proposerPubKey []byte, pubsubServer *pubsub.Server, logger log.Logger) error {
conf := slmock.Config{
Config: &settlement.Config{
BatchSize: batchSize,
},
ProposerPubKey: proposerPubKey,
}
byteconf, _ := json.Marshal(conf)
return settlementlc.Init(byteconf, pubsubServer, logger)
err := settlementlc.Init(byteconf, pubsubServer, logger)
if err != nil {
return err
}
err = settlementlc.Start()
if err != nil {
return err
}
return nil
}

func getManagerConfig() config.BlockManagerConfig {
Expand Down
31 changes: 27 additions & 4 deletions rpc/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"context"
crand "crypto/rand"
"encoding/json"
"fmt"
"math/rand"
"testing"
Expand All @@ -28,6 +29,7 @@ import (
abciconv "github.com/dymensionxyz/dymint/conv/abci"
"github.com/dymensionxyz/dymint/mocks"
"github.com/dymensionxyz/dymint/node"
slmock "github.com/dymensionxyz/dymint/settlement/mock"
"github.com/dymensionxyz/dymint/types"
)

Expand Down Expand Up @@ -404,15 +406,23 @@ func TestTx(t *testing.T) {
mockApp := &mocks.Application{}
mockApp.On("InitChain", mock.Anything).Return(abci.ResponseInitChain{})
key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
signingKey, _, _ := crypto.GenerateEd25519Key(crand.Reader)
signingKey, proposerPubKey, err := crypto.GenerateEd25519Key(crand.Reader)
require.NoError(err)

proposerPubKeyBytes, err := proposerPubKey.Raw()
settlementLayerConfig, err := json.Marshal(slmock.Config{ProposerPubKey: proposerPubKeyBytes})
require.NoError(err)

node, err := node.NewNode(context.Background(), config.NodeConfig{
DALayer: "mock",
SettlementLayer: "mock",
Aggregator: true,
BlockManagerConfig: config.BlockManagerConfig{
BlockTime: 200 * time.Millisecond,
BatchSyncInterval: time.Second,
}},
},
SettlementConfig: string(settlementLayerConfig),
},
key, signingKey, proxy.NewLocalClientCreator(mockApp),
&tmtypes.GenesisDoc{ChainID: "test"},
log.TestingLogger())
Expand Down Expand Up @@ -644,7 +654,12 @@ func TestValidatorSetHandling(t *testing.T) {
app.On("Info", mock.Anything).Return(abci.ResponseInfo{LastBlockHeight: 0, LastBlockAppHash: []byte{0}})

key, _, _ := crypto.GenerateEd25519Key(crand.Reader)
signingKey, _, _ := crypto.GenerateEd25519Key(crand.Reader)
signingKey, proposerPubKey, err := crypto.GenerateEd25519Key(crand.Reader)
require.NoError(err)

proposerPubKeyBytes, err := proposerPubKey.Raw()
settlementLayerConfig, err := json.Marshal(slmock.Config{ProposerPubKey: proposerPubKeyBytes})
require.NoError(err)

vKeys := make([]tmcrypto.PrivKey, 4)
genesisValidators := make([]tmtypes.GenesisValidator, len(vKeys))
Expand All @@ -666,7 +681,15 @@ func TestValidatorSetHandling(t *testing.T) {
waitCh <- nil
})

node, err := node.NewNode(context.Background(), config.NodeConfig{DALayer: "mock", SettlementLayer: "mock", Aggregator: true, BlockManagerConfig: config.BlockManagerConfig{BlockTime: 10 * time.Millisecond, BatchSyncInterval: time.Second}}, key, signingKey, proxy.NewLocalClientCreator(app), &tmtypes.GenesisDoc{ChainID: "test", Validators: genesisValidators}, log.TestingLogger())
nodeConfig := config.NodeConfig{
DALayer: "mock",
SettlementLayer: "mock",
Aggregator: true,
BlockManagerConfig: config.BlockManagerConfig{BlockTime: 10 * time.Millisecond, BatchSyncInterval: time.Second},
SettlementConfig: string(settlementLayerConfig),
}

node, err := node.NewNode(context.Background(), nodeConfig, key, signingKey, proxy.NewLocalClientCreator(app), &tmtypes.GenesisDoc{ChainID: "test", Validators: genesisValidators}, log.TestingLogger())
require.NoError(err)
require.NotNil(node)

Expand Down
8 changes: 6 additions & 2 deletions rpc/json/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/dymensionxyz/dymint/mocks"
"github.com/dymensionxyz/dymint/node"
"github.com/dymensionxyz/dymint/rpc/client"
slmock "github.com/dymensionxyz/dymint/settlement/mock"
)

func TestHandlerMapping(t *testing.T) {
Expand Down Expand Up @@ -289,8 +290,11 @@ func getRPC(t *testing.T) (*mocks.Application, *client.Client) {
LastBlockAppHash: nil,
})
key, _, _ := crypto.GenerateEd25519Key(rand.Reader)
signingKey, _, _ := crypto.GenerateEd25519Key(rand.Reader)
config := config.NodeConfig{Aggregator: true, DALayer: "mock", SettlementLayer: "mock", BlockManagerConfig: config.BlockManagerConfig{BlockTime: 1 * time.Second, BatchSyncInterval: time.Second}}
signingKey, proposerPubKey, _ := crypto.GenerateEd25519Key(rand.Reader)
proposerPubKeyBytes, err := proposerPubKey.Raw()
settlementLayerConfig, err := json.Marshal(slmock.Config{ProposerPubKey: proposerPubKeyBytes})
require.NoError(err)
config := config.NodeConfig{Aggregator: true, DALayer: "mock", SettlementLayer: "mock", BlockManagerConfig: config.BlockManagerConfig{BlockTime: 1 * time.Second, BatchSyncInterval: time.Second}, SettlementConfig: string(settlementLayerConfig)}
node, err := node.NewNode(context.Background(), config, key, signingKey, proxy.NewLocalClientCreator(app), &types.GenesisDoc{ChainID: "test"}, log.TestingLogger())
require.NoError(err)
require.NotNil(node)
Expand Down
2 changes: 1 addition & 1 deletion rpc/json/ws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestWebSockets(t *testing.T) {
`))
assert.NoError(err)

err = conn.SetReadDeadline(time.Now().Add(1 * time.Second))
err = conn.SetReadDeadline(time.Now().Add(100 * time.Second))
assert.NoError(err)
typ, msg, err := conn.ReadMessage()
assert.NoError(err)
Expand Down
12 changes: 7 additions & 5 deletions settlement/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"errors"
"sync/atomic"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
rollapptypes "github.com/dymensionxyz/dymension/x/rollapp/types"
"github.com/dymensionxyz/dymint/da"
"github.com/dymensionxyz/dymint/log"
Expand Down Expand Up @@ -56,9 +56,10 @@ func (m *SettlementLayerClient) Init(config []byte, pubsub *pubsub.Server, logge
// Config for the HubClient
type Config struct {
*settlement.Config
DBPath string `json:"db_path"`
RootDir string `json:"root_dir"`
store store.KVStore
DBPath string `json:"db_path"`
RootDir string `json:"root_dir"`
ProposerPubKey []byte `json:"proposer_pub_key"`
store store.KVStore
}

// HubClient implements The HubClient interface
Expand Down Expand Up @@ -202,9 +203,10 @@ func (c *HubClient) GetBatchAtIndex(rollappID string, index uint64) (*settlement

// GetSequencers returns a list of sequencers. Currently only returns a single sequencer
func (c *HubClient) GetSequencers(rollappID string) ([]*types.Sequencer, error) {
pubKey := &ed25519.PubKey{Key: c.config.ProposerPubKey}
return []*types.Sequencer{
{
PublicKey: secp256k1.GenPrivKey().PubKey(),
PublicKey: pubKey,
Status: types.Proposer,
},
}, nil
Expand Down
6 changes: 5 additions & 1 deletion settlement/settlement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package settlement_test
import (
"testing"

"github.com/libp2p/go-libp2p-core/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/pubsub"
Expand Down Expand Up @@ -97,7 +98,10 @@ func TestSubmitAndRetrieve(t *testing.T) {
for i := 0; i < numBatches; i++ {
startHeight := uint64(i)*batchSize + 1
// Create the batch
batch = testutil.GenerateBatch(startHeight, uint64(startHeight+batchSize-1))
propserKey, _, err := crypto.GenerateEd25519Key(nil)
require.NoError(err)
batch, err = testutil.GenerateBatch(startHeight, uint64(startHeight+batchSize-1), propserKey)
require.NoError(err)
// Submit the batch
daResult := &da.ResultSubmitBatch{
BaseResult: da.BaseResult{
Expand Down
23 changes: 19 additions & 4 deletions state/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ func (e *BlockExecutor) CreateBlock(height uint64, lastCommit *types.Commit, las
}

// ApplyBlock validates and executes the block.
func (e *BlockExecutor) ApplyBlock(ctx context.Context, state types.State, block *types.Block) (types.State, *tmstate.ABCIResponses, error) {
err := e.validate(state, block)
func (e *BlockExecutor) ApplyBlock(ctx context.Context, state types.State, block *types.Block, commit *types.Commit, proposer *types.Sequencer) (types.State, *tmstate.ABCIResponses, error) {
err := e.validateBlock(state, block)
if err != nil {
return types.State{}, nil, err
}
err = e.validateCommit(proposer, commit, &block.Header)
if err != nil {
return types.State{}, nil, err
}

// This makes calls to the ProxyApp
resp, err := e.execute(ctx, state, block)
if err != nil {
Expand Down Expand Up @@ -253,7 +256,7 @@ func (e *BlockExecutor) commit(ctx context.Context, state *types.State, block *t
return resp.Data, err
}

func (e *BlockExecutor) validate(state types.State, block *types.Block) error {
func (e *BlockExecutor) validateBlock(state types.State, block *types.Block) error {
err := block.ValidateBasic()
if err != nil {
return err
Expand All @@ -279,6 +282,18 @@ func (e *BlockExecutor) validate(state types.State, block *types.Block) error {
return nil
}

func (e *BlockExecutor) validateCommit(proposer *types.Sequencer, commit *types.Commit, header *types.Header) error {
abciHeaderPb := abciconv.ToABCIHeaderPB(header)
abciHeaderBytes, err := abciHeaderPb.Marshal()
if err != nil {
return err
}
if err = commit.Validate(proposer, abciHeaderBytes); err != nil {
return err
}
return nil
}

func (e *BlockExecutor) execute(ctx context.Context, state types.State, block *types.Block) (*tmstate.ABCIResponses, error) {
abciResponses := new(tmstate.ABCIResponses)
abciResponses.DeliverTxs = make([]*abci.ResponseDeliverTx, len(block.Data.Txs))
Expand Down
Loading

0 comments on commit 2fb8663

Please sign in to comment.