Skip to content

Commit

Permalink
testing (core): move existing tests to integration tests, rewrite `me…
Browse files Browse the repository at this point in the history
…ssage.go` unit tests (#2224)

* chore(pkg/scale): return error if `Result` already has an assigned value  (#2143)

* fix: return error if result already has an assigned value

* chore: include unit test

* chore: fix typo `ErrResultAlreadySet`

* chore: remove unneeded `require.Error`

* chore: fix undeclared name

* chore: remove packaged scope var to avoid problems with result type

* chore: fix result.Set error at offchain test

* migrate core tests to integration tests

* remove test.log

* upgrade integration tests to match dev

* testing (dot/core): rewrite message.go unit tests (#2197)

* chore(pkg/scale): return error if `Result` already has an assigned value  (#2143)

* fix: return error if result already has an assigned value

* chore: include unit test

* chore: fix typo `ErrResultAlreadySet`

* chore: remove unneeded `require.Error`

* chore: fix undeclared name

* chore: remove packaged scope var to avoid problems with result type

* chore: fix result.Set error at offchain test

* test txt count and generate mocks with mockgen

* WIP/hanldeTxnMsgTest

* WIP/core message tests

* wip/finish core message tests

* wip/message test

* fix reporting issue

* test core messages

* remove comments and lint

* remove unused file

* wip/cr feedback

* use dummy error for tests

* wip/finish cr feedback

* wip/move message validation to a separate function

* move txn validity check to new func

* lint

* fix variable naming to make less confusing

* remove pointer from validateTxn helper params

* refactor tests to define mocks in subtest

* CR feedback

* finish feedback for core tests

* define runtime mocks in subtest

* remane validTxn var

Co-authored-by: Eclésio Junior <[email protected]>

* CR feedback

* finish feedback

* CR feedback

Co-authored-by: Eclésio Junior <[email protected]>
  • Loading branch information
jimjbrettj and EclesioMeloJunior authored Jan 21, 2022
1 parent fd60061 commit 0a25d1a
Show file tree
Hide file tree
Showing 6 changed files with 1,397 additions and 189 deletions.
8 changes: 1 addition & 7 deletions dot/core/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/ChainSafe/gossamer/lib/transaction"
)

//go:generate mockery --name BlockState --structname BlockState --case underscore --keeptree
//go:generate mockgen -destination=mock_core_test.go -package $GOPACKAGE . BlockState,StorageState,TransactionState,Network,EpochState,CodeSubstitutedState,DigestHandler

// BlockState interface for block state methods
type BlockState interface {
Expand Down Expand Up @@ -47,8 +47,6 @@ type BlockState interface {
StoreRuntime(common.Hash, runtime.Instance)
}

//go:generate mockery --name StorageState --structname StorageState --case underscore --keeptree

// StorageState interface for storage state methods
type StorageState interface {
LoadCode(root *common.Hash) ([]byte, error)
Expand All @@ -70,8 +68,6 @@ type TransactionState interface {
PendingInPool() []*transaction.ValidTransaction
}

//go:generate mockery --name Network --structname Network --case underscore --keeptree

// Network is the interface for the network service
type Network interface {
GossipMessage(network.NotificationsMessage)
Expand All @@ -92,8 +88,6 @@ type CodeSubstitutedState interface {
StoreCodeSubstitutedBlockHash(hash common.Hash) error
}

//go:generate mockery --name DigestHandler --structname DigestHandler --case underscore --keeptree

// DigestHandler is the interface for the consensus digest handler
type DigestHandler interface {
HandleDigests(header *types.Header)
Expand Down
100 changes: 54 additions & 46 deletions dot/core/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,53 @@ package core

import (
"errors"

"github.com/libp2p/go-libp2p-core/peer"
"fmt"

"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/peerset"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/transaction"

"github.com/libp2p/go-libp2p-core/peer"
)

func (s *Service) validateTransaction(peerID peer.ID, head *types.Header, rt runtime.Instance,
tx types.Extrinsic) (validity *transaction.Validity, valid bool, err error) {
s.storageState.Lock()

ts, err := s.storageState.TrieState(&head.StateRoot)
s.storageState.Unlock()
if err != nil {
return nil, false, fmt.Errorf("cannot get trie state from storage for root %s: %w", head.StateRoot, err)
}

rt.SetContextStorage(ts)

// validate each transaction
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, tx...))
validity, err = rt.ValidateTransaction(externalExt)
if err != nil {
if errors.Is(err, runtime.ErrInvalidTransaction) {
s.net.ReportPeer(peerset.ReputationChange{
Value: peerset.BadTransactionValue,
Reason: peerset.BadTransactionReason,
}, peerID)
}

logger.Debugf("failed to validate transaction: %s", err)
return nil, false, nil
}

vtx := transaction.NewValidTransaction(tx, validity)

// push to the transaction queue of BABE session
hash := s.transactionState.AddToPool(vtx)
logger.Tracef("added transaction with hash %s to pool", hash)

return validity, true, nil
}

// HandleTransactionMessage validates each transaction in the message and
// adds valid transactions to the transaction queue of the BABE session
// returns boolean for transaction propagation, true - transactions should be propagated
Expand All @@ -41,56 +78,27 @@ func (s *Service) HandleTransactionMessage(peerID peer.ID, msg *network.Transact
return false, err
}

allTxsAreValid := true
for _, tx := range txs {
err = func() error {
s.storageState.Lock()
defer s.storageState.Unlock()

ts, err := s.storageState.TrieState(&head.StateRoot)
if err != nil {
return err
}

rt.SetContextStorage(ts)

// validate each transaction
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, tx...))
val, err := rt.ValidateTransaction(externalExt)
if err != nil {
if errors.Is(err, runtime.ErrInvalidTransaction) {
s.net.ReportPeer(peerset.ReputationChange{
Value: peerset.BadTransactionValue,
Reason: peerset.BadTransactionReason,
}, peerID)
}
logger.Debugf("failed to validate transaction: %s", err)
return nil
}

// create new valid transaction
vtx := transaction.NewValidTransaction(tx, val)

// push to the transaction queue of BABE session
hash := s.transactionState.AddToPool(vtx)
logger.Tracef("added transaction with hash %s to pool", hash)
validity, isValidTxn, err := s.validateTransaction(peerID, head, rt, tx)
if err != nil {
return false, fmt.Errorf("failed validating transaction for peerID %s: %w", peerID, err)
}

if !isValidTxn {
allTxsAreValid = false
} else if validity.Propagate {
// find tx(s) that should propagate
if val.Propagate {
toPropagate = append(toPropagate, tx)
}

return nil
}()

if err != nil {
return false, err
toPropagate = append(toPropagate, tx)
}
}

s.net.ReportPeer(peerset.ReputationChange{
Value: peerset.GoodTransactionValue,
Reason: peerset.GoodTransactionReason,
}, peerID)
if allTxsAreValid {
s.net.ReportPeer(peerset.ReputationChange{
Value: peerset.GoodTransactionValue,
Reason: peerset.GoodTransactionReason,
}, peerID)
}

msg.Extrinsics = toPropagate
return len(msg.Extrinsics) > 0, nil
Expand Down
172 changes: 172 additions & 0 deletions dot/core/messages_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

//go:build integration
// +build integration

package core

import (
"math/big"
"testing"
"time"

"github.com/centrifuge/go-substrate-rpc-client/v3/signature"
ctypes "github.com/centrifuge/go-substrate-rpc-client/v3/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"

"github.com/ChainSafe/gossamer/dot/core/mocks"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/sync"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/pkg/scale"
)

func createExtrinsic(t *testing.T, rt runtime.Instance, genHash common.Hash, nonce uint64) types.Extrinsic {
t.Helper()
rawMeta, err := rt.Metadata()
require.NoError(t, err)

var decoded []byte
err = scale.Unmarshal(rawMeta, &decoded)
require.NoError(t, err)

meta := &ctypes.Metadata{}
err = ctypes.DecodeFromBytes(decoded, meta)
require.NoError(t, err)

rv, err := rt.Version()
require.NoError(t, err)

c, err := ctypes.NewCall(meta, "System.remark", []byte{0xab, 0xcd})
require.NoError(t, err)

ext := ctypes.NewExtrinsic(c)
options := ctypes.SignatureOptions{
BlockHash: ctypes.Hash(genHash),
Era: ctypes.ExtrinsicEra{IsImmortalEra: false},
GenesisHash: ctypes.Hash(genHash),
Nonce: ctypes.NewUCompactFromUInt(nonce),
SpecVersion: ctypes.U32(rv.SpecVersion()),
Tip: ctypes.NewUCompactFromUInt(0),
TransactionVersion: ctypes.U32(rv.TransactionVersion()),
}

// Sign the transaction using Alice's key
err = ext.Sign(signature.TestKeyringPairAlice, options)
require.NoError(t, err)

extEnc, err := ctypes.EncodeToHexString(ext)
require.NoError(t, err)

extBytes := types.Extrinsic(common.MustHexToBytes(extEnc))
return extBytes
}

func TestService_HandleBlockProduced(t *testing.T) {
net := new(mocks.Network)
cfg := &Config{
Network: net,
Keystore: keystore.NewGlobalKeystore(),
}

s := NewTestService(t, cfg)
err := s.Start()
require.NoError(t, err)

// simulate block sent from BABE session
digest := types.NewDigest()
prd, err := types.NewBabeSecondaryPlainPreDigest(0, 1).ToPreRuntimeDigest()
require.NoError(t, err)
err = digest.Add(*prd)
require.NoError(t, err)

newBlock := types.Block{
Header: types.Header{
Number: big.NewInt(1),
ParentHash: s.blockState.BestBlockHash(),
Digest: digest,
},
Body: *types.NewBody([]types.Extrinsic{}),
}

expected := &network.BlockAnnounceMessage{
ParentHash: newBlock.Header.ParentHash,
Number: newBlock.Header.Number,
StateRoot: newBlock.Header.StateRoot,
ExtrinsicsRoot: newBlock.Header.ExtrinsicsRoot,
Digest: digest,
BestBlock: true,
}

net.On("GossipMessage", expected)

state, err := s.storageState.TrieState(nil)
require.NoError(t, err)

err = s.HandleBlockProduced(&newBlock, state)
require.NoError(t, err)

time.Sleep(time.Second)
net.AssertCalled(t, "GossipMessage", expected)
}

func TestService_HandleTransactionMessage(t *testing.T) {
t.Parallel()

const peer1 = "testPeer1"

kp, err := sr25519.GenerateKeypair()
require.NoError(t, err)

ks := keystore.NewGlobalKeystore()
ks.Acco.Insert(kp)

ctrl := gomock.NewController(t)
telemetryMock := NewMockClient(ctrl)
telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes()

cfg := &Config{
Keystore: ks,
TransactionState: state.NewTransactionState(telemetryMock),
}

s := NewTestService(t, cfg)
genHash := s.blockState.GenesisHash()
genHeader, err := s.blockState.BestBlockHeader()
require.NoError(t, err)

rt, err := s.blockState.GetRuntime(nil)
require.NoError(t, err)

ts, err := s.storageState.TrieState(nil)
require.NoError(t, err)
rt.SetContextStorage(ts)

block := sync.BuildBlock(t, rt, genHeader, nil)

err = s.handleBlock(block, ts)
require.NoError(t, err)

extBytes := createExtrinsic(t, rt, genHash, 0)
msg := &network.TransactionMessage{Extrinsics: []types.Extrinsic{extBytes}}
shouldPropagate, err := s.HandleTransactionMessage(peer1, msg)
require.NoError(t, err)
require.True(t, shouldPropagate)

pending := s.transactionState.(*state.TransactionState).Pending()
require.NotEmpty(t, pending)
require.Equal(t, extBytes, pending[0].Extrinsic)

extBytes = []byte(`bogus extrinsic`)
msg = &network.TransactionMessage{Extrinsics: []types.Extrinsic{extBytes}}
shouldPropagate, err = s.HandleTransactionMessage(peer1, msg)
require.NoError(t, err)
require.False(t, shouldPropagate)
}
Loading

0 comments on commit 0a25d1a

Please sign in to comment.