Skip to content

Commit

Permalink
fix(core): fix txn pool for latest runtime (#2809)
Browse files Browse the repository at this point in the history
Fix transaction formatting, fix txnPool, and redo core service orchestrations for service tests
  • Loading branch information
jimjbrettj committed Oct 24, 2022
1 parent 57168fc commit 1551e66
Show file tree
Hide file tree
Showing 16 changed files with 539 additions and 230 deletions.
9 changes: 2 additions & 7 deletions dot/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,12 @@ import (
)

var (

// ErrServiceStopped is returned when the service has been stopped
ErrServiceStopped = errors.New("service has been stopped")

// ErrInvalidBlock is returned when a block cannot be verified
ErrInvalidBlock = errors.New("could not verify block")

ErrNilRuntime = errors.New("cannot have nil runtime")

ErrNilBlockHandlerParameter = errors.New("unable to handle block due to nil parameter")

// ErrEmptyRuntimeCode is returned when the storage :code is empty
ErrEmptyRuntimeCode = errors.New("new :code is empty")

errInvalidTransactionQueueVersion = errors.New("invalid transaction queue version")
)
125 changes: 125 additions & 0 deletions dot/core/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
package core

import (
"bytes"
"path/filepath"
"testing"

"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/internal/log"
Expand All @@ -19,10 +21,133 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/ChainSafe/gossamer/lib/utils"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
)

func balanceKey(t *testing.T, pub []byte) (bKey []byte) {
t.Helper()

h0, err := common.Twox128Hash([]byte("System"))
require.NoError(t, err)
h1, err := common.Twox128Hash([]byte("Account"))
require.NoError(t, err)
h2, err := common.Blake2b128(pub)
require.NoError(t, err)
return bytes.Join([][]byte{h0, h1, h2, pub}, nil)
}

// Creates test service, used now for testing txnPool but can be used elsewhere when needed
func createTestService(t *testing.T, genesisFilePath string,
pubKey []byte, accountInfo types.AccountInfo, ctrl *gomock.Controller) (service *Service, encodedExtrinsic []byte) {
t.Helper()

gen, err := genesis.NewGenesisFromJSONRaw(genesisFilePath)
require.NoError(t, err)

genesisTrie, err := wasmer.NewTrieFromGenesis(*gen)
require.NoError(t, err)

// Extrinsic and context related stuff
aliceBalanceKey := balanceKey(t, pubKey)
encodedAccountInfo, err := scale.Marshal(accountInfo)
require.NoError(t, err)

genesisHeader := &types.Header{
StateRoot: genesisTrie.MustHash(),
Number: 0,
}

cfgKeystore := keystore.NewGlobalKeystore()
kp, err := sr25519.GenerateKeypair()
require.NoError(t, err)
err = cfgKeystore.Acco.Insert(kp)
require.NoError(t, err)

// Create state service
var stateSrvc *state.Service
testDatadirPath := t.TempDir()

// Set up block and storage state
telemetryMock := NewMockClient(ctrl)
telemetryMock.EXPECT().SendMessage(gomock.Any()).AnyTimes()

stateConfig := state.Config{
Path: testDatadirPath,
LogLevel: log.Critical,
Telemetry: telemetryMock,
}

stateSrvc = state.NewService(stateConfig)
stateSrvc.UseMemDB()

err = stateSrvc.Initialise(gen, genesisHeader, &genesisTrie)
require.NoError(t, err)

// Start state service
err = stateSrvc.Start()
require.NoError(t, err)

cfgBlockState := stateSrvc.Block
cfgStorageState := stateSrvc.Storage
cfgCodeSubstitutedState := stateSrvc.Base

var rtCfg wasmer.Config
rtCfg.Storage = rtstorage.NewTrieState(&genesisTrie)

rtCfg.CodeHash, err = cfgStorageState.LoadCodeHash(nil)
require.NoError(t, err)

nodeStorage := runtime.NodeStorage{}
nodeStorage.BaseDB = stateSrvc.Base

rtCfg.NodeStorage = nodeStorage

cfgRuntime, err := wasmer.NewRuntimeFromGenesis(rtCfg)
require.NoError(t, err)

cfgRuntime.(*wasmer.Instance).GetContext().Storage.Set(aliceBalanceKey, encodedAccountInfo)
// this key is System.UpgradedToDualRefCount -> set to true since all accounts have been upgraded to v0.9 format
cfgRuntime.(*wasmer.Instance).GetContext().Storage.Set(common.UpgradedToDualRefKey, []byte{1})

cfgBlockState.StoreRuntime(cfgBlockState.BestBlockHash(), cfgRuntime)

// Hash of encrypted centrifuge extrinsic
testCallArguments := []byte{0xab, 0xcd}
extHex := runtime.NewTestExtrinsic(t, cfgRuntime, genesisHeader.Hash(), cfgBlockState.BestBlockHash(),
0, "System.remark", testCallArguments)
encodedExtrinsic = common.MustHexToBytes(extHex)

cfgCodeSubstitutes := make(map[common.Hash]string)

genesisData, err := cfgCodeSubstitutedState.LoadGenesisData()
require.NoError(t, err)

for k, v := range genesisData.CodeSubstitutes {
cfgCodeSubstitutes[common.MustHexToHash(k)] = v
}

cfgCodeSubstitutedState = stateSrvc.Base

cfg := &Config{
Keystore: cfgKeystore,
LogLvl: log.Critical,
BlockState: cfgBlockState,
StorageState: cfgStorageState,
TransactionState: stateSrvc.Transaction,
EpochState: stateSrvc.Epoch,
CodeSubstitutedState: cfgCodeSubstitutedState,
Runtime: cfgRuntime,
Network: new(network.Service),
CodeSubstitutes: cfgCodeSubstitutes,
}
service, err = NewService(cfg)
require.NoError(t, err)

return service, encodedExtrinsic
}

// NewTestService creates a new test core service
func NewTestService(t *testing.T, cfg *Config) *Service {
t.Helper()
Expand Down
13 changes: 8 additions & 5 deletions dot/core/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ func (s *Service) validateTransaction(head *types.Header, rt RuntimeInstance,
rt.SetContextStorage(ts)

// validate each transaction
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, tx...))
externalExt, err := s.buildExternalTransaction(rt, tx)
if err != nil {
return nil, fmt.Errorf("building external transaction: %w", err)
}

validity, err = rt.ValidateTransaction(externalExt)
if err != nil {
logger.Debugf("failed to validate transaction: %s", err)
Expand Down Expand Up @@ -72,12 +76,10 @@ func (s *Service) HandleTransactionMessage(peerID peer.ID, msg *network.Transact

allTxnsAreValid := true
for _, tx := range txs {
txnIsValid := true
validity, err := s.validateTransaction(head, rt, tx)
if err != nil {
txnIsValid = false
allTxnsAreValid = false
switch err := err.(type) {
switch err.(type) {
case runtime.InvalidTransaction:
s.net.ReportPeer(peerset.ReputationChange{
Value: peerset.BadTransactionValue,
Expand All @@ -87,9 +89,10 @@ func (s *Service) HandleTransactionMessage(peerID peer.ID, msg *network.Transact
default:
return false, fmt.Errorf("validating transaction from peerID %s: %w", peerID, err)
}
continue
}

if txnIsValid && validity.Propagate {
if validity.Propagate {
// find tx(s) that should propagate
toPropagate = append(toPropagate, tx)
}
Expand Down
8 changes: 7 additions & 1 deletion dot/core/messages_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,17 @@ func TestService_HandleBlockProduced(t *testing.T) {
err = digest.Add(*prd)
require.NoError(t, err)

// Used to define the state root of new block for testing
parentHash := s.blockState.GenesisHash()
genesisBlock, err := s.blockState.GetBlockByHash(parentHash)
require.NoError(t, err)

newBlock := types.Block{
Header: types.Header{
Number: 1,
ParentHash: s.blockState.BestBlockHash(),
ParentHash: parentHash,
Digest: digest,
StateRoot: genesisBlock.Header.StateRoot,
},
Body: *types.NewBody([]types.Extrinsic{}),
}
Expand Down
38 changes: 33 additions & 5 deletions dot/core/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package core

import (
"bytes"
"errors"
"testing"

Expand Down Expand Up @@ -44,8 +45,9 @@ type mockGetRuntime struct {
}

type mockBlockState struct {
bestHeader *mockBestHeader
getRuntime *mockGetRuntime
bestHeader *mockBestHeader
getRuntime *mockGetRuntime
callsBestBlockHash bool
}

type mockStorageState struct {
Expand Down Expand Up @@ -252,6 +254,7 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
getRuntime: &mockGetRuntime{
runtime: runtimeMock2,
},
callsBestBlockHash: true,
},
mockStorageState: &mockStorageState{
input: &common.Hash{},
Expand All @@ -261,8 +264,12 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
runtime: runtimeMock2,
setContextStorage: &mockSetContextStorage{trieState: &storage.TrieState{}},
validateTxn: &mockValidateTxn{
input: types.Extrinsic(append([]byte{byte(types.TxnExternal)}, testExtrinsic[0]...)),
err: invalidTransaction,
input: types.Extrinsic(bytes.Join([][]byte{
{byte(types.TxnExternal)},
testExtrinsic[0],
testEmptyHeader.StateRoot.ToBytes(),
}, nil)),
err: invalidTransaction,
},
},
args: args{
Expand Down Expand Up @@ -291,6 +298,7 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
getRuntime: &mockGetRuntime{
runtime: runtimeMock3,
},
callsBestBlockHash: true,
},
mockStorageState: &mockStorageState{
input: &common.Hash{},
Expand All @@ -308,7 +316,11 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
runtime: runtimeMock3,
setContextStorage: &mockSetContextStorage{trieState: &storage.TrieState{}},
validateTxn: &mockValidateTxn{
input: types.Extrinsic(append([]byte{byte(types.TxnExternal)}, testExtrinsic[0]...)),
input: types.Extrinsic(bytes.Join([][]byte{
{byte(types.TxnExternal)},
testExtrinsic[0],
testEmptyHeader.StateRoot.ToBytes(),
}, nil)),
validity: &transaction.Validity{Propagate: true},
},
},
Expand Down Expand Up @@ -344,6 +356,9 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
tt.mockBlockState.getRuntime.runtime,
tt.mockBlockState.getRuntime.err)
}
if tt.mockBlockState.callsBestBlockHash {
blockState.EXPECT().BestBlockHash().Return(common.Hash{})
}
s.blockState = blockState
}
if tt.mockStorageState != nil {
Expand All @@ -365,6 +380,19 @@ func TestServiceHandleTransactionMessage(t *testing.T) {
rt.EXPECT().SetContextStorage(tt.mockRuntime.setContextStorage.trieState)
rt.EXPECT().ValidateTransaction(tt.mockRuntime.validateTxn.input).
Return(tt.mockRuntime.validateTxn.validity, tt.mockRuntime.validateTxn.err)
rt.EXPECT().Version().Return(runtime.Version{
SpecName: []byte("polkadot"),
ImplName: []byte("parity-polkadot"),
AuthoringVersion: authoringVersion,
SpecVersion: specVersion,
ImplVersion: implVersion,
APIItems: []runtime.APIItem{{
Name: common.MustBlake2b8([]byte("TaggedTransactionQueue")),
Ver: 3,
}},
TransactionVersion: transactionVersion,
StateVersion: stateVersion,
})
}

res, err := s.HandleTransactionMessage(tt.args.peerID, tt.args.msg)
Expand Down
Loading

0 comments on commit 1551e66

Please sign in to comment.