// Copyright 2021 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package wazero_runtime

import (
	"bytes"
	"encoding/json"
	"math/big"
	"os"
	"path/filepath"
	"testing"

	"github.com/ChainSafe/gossamer/dot/network/messages"
	"github.com/ChainSafe/gossamer/dot/types"
	"github.com/ChainSafe/gossamer/internal/database"
	"github.com/ChainSafe/gossamer/internal/log"
	"github.com/ChainSafe/gossamer/lib/common"
	"github.com/ChainSafe/gossamer/lib/crypto/ed25519"
	"github.com/ChainSafe/gossamer/lib/genesis"
	"github.com/ChainSafe/gossamer/lib/runtime"
	"github.com/ChainSafe/gossamer/lib/runtime/storage"
	"github.com/ChainSafe/gossamer/lib/runtime/wazero/testdata"
	"github.com/ChainSafe/gossamer/lib/utils"
	"github.com/ChainSafe/gossamer/pkg/scale"
	"github.com/ChainSafe/gossamer/pkg/trie"
	inmemory_trie "github.com/ChainSafe/gossamer/pkg/trie/inmemory"
	"github.com/centrifuge/go-substrate-rpc-client/v4/signature"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func mustHexTo64BArray(t *testing.T, inputHex string) (outputArray [64]byte) {
	t.Helper()
	copy(outputArray[:], common.MustHexToBytes(inputHex))
	return outputArray
}

func genesisFromRawJSON(t *testing.T, jsonFilepath string) (gen genesis.Genesis) {
	t.Helper()

	fp, err := filepath.Abs(jsonFilepath)
	require.NoError(t, err)

	data, err := os.ReadFile(filepath.Clean(fp))
	require.NoError(t, err)

	err = json.Unmarshal(data, &gen)
	require.NoError(t, err)

	return gen
}

func newTrieFromKeyValueList(t *testing.T, filename string) trie.Trie {
	data, err := os.ReadFile(filename)
	require.NoError(t, err)

	stateTrieResponse := make([]string, 0)
	err = json.Unmarshal(data, &stateTrieResponse)
	require.NoError(t, err)

	entries := make(map[string]string)
	for _, encEntry := range stateTrieResponse {
		entry := new(trie.Entry)
		err := scale.Unmarshal(common.MustHexToBytes(encEntry), entry)
		require.NoError(t, err)

		entries[common.BytesToHex(entry.Key)] = common.BytesToHex(entry.Value)
	}

	tr, err := inmemory_trie.LoadFromMap(entries, trie.V0)
	require.NoError(t, err)
	return tr
}

// this is generated by printing key ownership proof while running `test_generate_equivocation_report_blob`
// https://github.com/paritytech/substrate/blob/ded44948e2d5a398abcb4e342b0513cb690961bb/frame/grandpa/src/benchmarking.rs#L85
var testKeyOwnershipProof types.OpaqueKeyOwnershipProof = types.OpaqueKeyOwnershipProof([]byte{64, 138, 252, 29, 127, 102, 189, 129, 207, 47, 157, 60, 17, 138, 194, 121, 139, 92, 176, 175, 224, 16, 185, 93, 175, 251, 224, 81, 209, 61, 0, 71}) //nolint:lll

func Test_Instance_Version(t *testing.T) {
	type instanceVersioner interface {
		Version() (runtime.Version, error)
	}

	testCases := map[string]struct {
		instanceBuilder func(t *testing.T) instanceVersioner
		expectedVersion runtime.Version
	}{
		"kusama": {
			instanceBuilder: func(t *testing.T) instanceVersioner {
				genesisPath := utils.GetKusamaGenesisPath(t)
				kusamaGenesis := genesisFromRawJSON(t, genesisPath)
				genesisTrie, err := runtime.NewTrieFromGenesis(kusamaGenesis)
				require.NoError(t, err)

				cfg := Config{
					Storage: storage.NewInMemoryTrieState(genesisTrie),
					LogLvl:  log.Critical,
				}

				instance, err := NewRuntimeFromGenesis(cfg)
				require.NoError(t, err)
				return instance
			},
			expectedVersion: runtime.Version{
				SpecName:         []byte("kusama"),
				ImplName:         []byte("parity-kusama"),
				AuthoringVersion: 2,
				SpecVersion:      1020,
				ImplVersion:      0,
				APIItems: []runtime.APIItem{
					{Name: [8]uint8{0xdf, 0x6a, 0xcb, 0x68, 0x99, 0x7, 0x60, 0x9b}, Ver: 0x2},
					{Name: [8]uint8{0x37, 0xe3, 0x97, 0xfc, 0x7c, 0x91, 0xf5, 0xe4}, Ver: 0x1},
					{Name: [8]uint8{0x40, 0xfe, 0x3a, 0xd4, 0x1, 0xf8, 0x95, 0x9a}, Ver: 0x4},
					{Name: [8]uint8{0xd2, 0xbc, 0x98, 0x97, 0xee, 0xd0, 0x8f, 0x15}, Ver: 0x1},
					{Name: [8]uint8{0xf7, 0x8b, 0x27, 0x8b, 0xe5, 0x3f, 0x45, 0x4c}, Ver: 0x1},
					{Name: [8]uint8{0xaf, 0x2c, 0x2, 0x97, 0xa2, 0x3e, 0x6d, 0x3d}, Ver: 0x1},
					{Name: [8]uint8{0xed, 0x99, 0xc5, 0xac, 0xb2, 0x5e, 0xed, 0xf5}, Ver: 0x2},
					{Name: [8]uint8{0xcb, 0xca, 0x25, 0xe3, 0x9f, 0x14, 0x23, 0x87}, Ver: 0x1},
					{Name: [8]uint8{0x68, 0x7a, 0xd4, 0x4a, 0xd3, 0x7f, 0x3, 0xc2}, Ver: 0x1},
					{Name: [8]uint8{0xab, 0x3c, 0x5, 0x72, 0x29, 0x1f, 0xeb, 0x8b}, Ver: 0x1},
					{Name: [8]uint8{0xbc, 0x9d, 0x89, 0x90, 0x4f, 0x5b, 0x92, 0x3f}, Ver: 0x1},
					{Name: [8]uint8{0x37, 0xc8, 0xbb, 0x13, 0x50, 0xa9, 0xa2, 0xa8}, Ver: 0x1},
				},
			},
		},
		"polkadot_v0929": {
			instanceBuilder: func(t *testing.T) instanceVersioner {
				return NewTestInstance(t, runtime.POLKADOT_RUNTIME_v0929)
			},
			expectedVersion: runtime.Version{
				SpecName:         []byte("polkadot"),
				ImplName:         []byte("parity-polkadot"),
				AuthoringVersion: 0,
				SpecVersion:      9290,
				ImplVersion:      0,
				APIItems: []runtime.APIItem{
					{Name: [8]uint8{0xdf, 0x6a, 0xcb, 0x68, 0x99, 0x7, 0x60, 0x9b}, Ver: 0x4},
					{Name: [8]uint8{0x37, 0xe3, 0x97, 0xfc, 0x7c, 0x91, 0xf5, 0xe4}, Ver: 0x1},
					{Name: [8]uint8{0x40, 0xfe, 0x3a, 0xd4, 0x1, 0xf8, 0x95, 0x9a}, Ver: 0x6},
					{Name: [8]uint8{0x17, 0xa6, 0xbc, 0xd, 0x0, 0x62, 0xae, 0xb3}, Ver: 0x1},
					{Name: [8]uint8{0xd2, 0xbc, 0x98, 0x97, 0xee, 0xd0, 0x8f, 0x15}, Ver: 0x3},
					{Name: [8]uint8{0xf7, 0x8b, 0x27, 0x8b, 0xe5, 0x3f, 0x45, 0x4c}, Ver: 0x2},
					{Name: [8]uint8{0xaf, 0x2c, 0x2, 0x97, 0xa2, 0x3e, 0x6d, 0x3d}, Ver: 0x2},
					{Name: [8]uint8{0x49, 0xea, 0xaf, 0x1b, 0x54, 0x8a, 0xc, 0xb0}, Ver: 0x1},
					{Name: [8]uint8{0x91, 0xd5, 0xdf, 0x18, 0xb0, 0xd2, 0xcf, 0x58}, Ver: 0x1},
					{Name: [8]uint8{0xed, 0x99, 0xc5, 0xac, 0xb2, 0x5e, 0xed, 0xf5}, Ver: 0x3},
					{Name: [8]uint8{0xcb, 0xca, 0x25, 0xe3, 0x9f, 0x14, 0x23, 0x87}, Ver: 0x2},
					{Name: [8]uint8{0x68, 0x7a, 0xd4, 0x4a, 0xd3, 0x7f, 0x3, 0xc2}, Ver: 0x1},
					{Name: [8]uint8{0xab, 0x3c, 0x5, 0x72, 0x29, 0x1f, 0xeb, 0x8b}, Ver: 0x1},
					{Name: [8]uint8{0xbc, 0x9d, 0x89, 0x90, 0x4f, 0x5b, 0x92, 0x3f}, Ver: 0x1},
					{Name: [8]uint8{0x37, 0xc8, 0xbb, 0x13, 0x50, 0xa9, 0xa2, 0xa8}, Ver: 0x1},
					{Name: [8]uint8{0xf3, 0xff, 0x14, 0xd5, 0xab, 0x52, 0x70, 0x59}, Ver: 0x1},
				},
				TransactionVersion: 14,
			},
		},
		"westend_v0929": {
			instanceBuilder: func(t *testing.T) instanceVersioner {
				return NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
			},
			expectedVersion: runtime.Version{
				SpecName:         []byte("westend"),
				ImplName:         []byte("parity-westend"),
				AuthoringVersion: 2,
				SpecVersion:      9290,
				ImplVersion:      0,
				APIItems: []runtime.APIItem{
					{Name: [8]uint8{0xdf, 0x6a, 0xcb, 0x68, 0x99, 0x7, 0x60, 0x9b}, Ver: 0x4},
					{Name: [8]uint8{0x37, 0xe3, 0x97, 0xfc, 0x7c, 0x91, 0xf5, 0xe4}, Ver: 0x1},
					{Name: [8]uint8{0x40, 0xfe, 0x3a, 0xd4, 0x1, 0xf8, 0x95, 0x9a}, Ver: 0x6},
					{Name: [8]uint8{0xd2, 0xbc, 0x98, 0x97, 0xee, 0xd0, 0x8f, 0x15}, Ver: 0x3},
					{Name: [8]uint8{0xf7, 0x8b, 0x27, 0x8b, 0xe5, 0x3f, 0x45, 0x4c}, Ver: 0x2},
					{Name: [8]uint8{0xaf, 0x2c, 0x2, 0x97, 0xa2, 0x3e, 0x6d, 0x3d}, Ver: 0x2},
					{Name: [8]uint8{0x49, 0xea, 0xaf, 0x1b, 0x54, 0x8a, 0xc, 0xb0}, Ver: 0x1},
					{Name: [8]uint8{0x91, 0xd5, 0xdf, 0x18, 0xb0, 0xd2, 0xcf, 0x58}, Ver: 0x1},
					{Name: [8]uint8{0xed, 0x99, 0xc5, 0xac, 0xb2, 0x5e, 0xed, 0xf5}, Ver: 0x3},
					{Name: [8]uint8{0xcb, 0xca, 0x25, 0xe3, 0x9f, 0x14, 0x23, 0x87}, Ver: 0x2},
					{Name: [8]uint8{0x68, 0x7a, 0xd4, 0x4a, 0xd3, 0x7f, 0x3, 0xc2}, Ver: 0x1},
					{Name: [8]uint8{0xab, 0x3c, 0x5, 0x72, 0x29, 0x1f, 0xeb, 0x8b}, Ver: 0x1},
					{Name: [8]uint8{0xbc, 0x9d, 0x89, 0x90, 0x4f, 0x5b, 0x92, 0x3f}, Ver: 0x1},
					{Name: [8]uint8{0x37, 0xc8, 0xbb, 0x13, 0x50, 0xa9, 0xa2, 0xa8}, Ver: 0x1},
					{Name: [8]uint8{0xf3, 0xff, 0x14, 0xd5, 0xab, 0x52, 0x70, 0x59}, Ver: 0x1},
					{Name: [8]uint8{0x17, 0xa6, 0xbc, 0xd, 0x0, 0x62, 0xae, 0xb3}, Ver: 0x1},
				},
				TransactionVersion: 12,
			},
		},
	}

	for name, testCase := range testCases {
		testCase := testCase
		t.Run(name, func(t *testing.T) {
			instance := testCase.instanceBuilder(t)
			version, err := instance.Version()
			require.NoError(t, err)
			assert.Equal(t, testCase.expectedVersion, version)
		})
	}
}

func balanceKey(t *testing.T, pub []byte) []byte {
	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 append(append(append(h0, h1...), h2...), pub...)
}

func TestWestendRuntime_ValidateTransaction(t *testing.T) {
	genesisPath := utils.GetWestendDevRawGenesisPath(t)
	gen := genesisFromRawJSON(t, genesisPath)
	genTrie, err := runtime.NewTrieFromGenesis(gen)
	require.NoError(t, err)

	// set state to genesis state
	genState := storage.NewInMemoryTrieState(genTrie)

	cfg := Config{
		Storage: genState,
		LogLvl:  log.Critical,
	}

	nodeStorage := runtime.NodeStorage{}
	nodeStorage.BaseDB = runtime.NewInMemoryDB(t)
	cfg.NodeStorage = nodeStorage

	rt, err := NewRuntimeFromGenesis(cfg)
	require.NoError(t, err)

	alicePub := common.MustHexToBytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")
	aliceBalanceKey := balanceKey(t, alicePub)

	accInfo := types.AccountInfo{
		Nonce: 0,
		Data: types.AccountData{
			Free:       scale.MustNewUint128(big.NewInt(1152921504606846976)),
			Reserved:   scale.MustNewUint128(big.NewInt(0)),
			MiscFrozen: scale.MustNewUint128(big.NewInt(0)),
			FreeFrozen: scale.MustNewUint128(big.NewInt(0)),
		},
	}

	encBal, err := scale.Marshal(accInfo)
	require.NoError(t, err)

	rt.Context.Storage.Put(aliceBalanceKey, encBal)
	// this key is System.UpgradedToDualRefCount -> set to true since all accounts have been upgraded to v0.9 format
	rt.Context.Storage.Put(common.UpgradedToDualRefKey, []byte{1})

	genesisHeader := &types.Header{
		Number:    0,
		StateRoot: trie.V0.MustHash(genTrie), // Get right state version from runtime
	}

	extHex := runtime.NewTestExtrinsic(t, rt, genesisHeader.Hash(), genesisHeader.Hash(),
		0, signature.TestKeyringPairAlice, "System.remark", []byte{0xab, 0xcd})

	genesisHashBytes := genesisHeader.Hash().ToBytes()

	validateTransactionArguments := [][]byte{
		{byte(types.TxnExternal)},
		common.MustHexToBytes(extHex),
		genesisHashBytes}

	extrinsicsBytes := bytes.Join(validateTransactionArguments, nil)

	runtime.InitializeRuntimeToTest(t, rt, genesisHeader)
	_, err = rt.ValidateTransaction(extrinsicsBytes)
	require.NoError(t, err)
}

func TestInstance_GrandpaAuthorities_NodeRuntime(t *testing.T) {
	tt := inmemory_trie.NewEmptyTrie()

	value, err := common.HexToBytes("0x0108eea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d714103640100000000000000b64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d7170100000000000000") //nolint:lll
	require.NoError(t, err)

	key := common.MustHexToBytes(genesis.GrandpaAuthoritiesKeyHex)
	tt.Put(key, value)

	rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929, TestWithTrie(tt))

	auths, err := rt.GrandpaAuthorities()
	require.NoError(t, err)

	authABytes, _ := common.HexToBytes("0xeea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d71410364")
	authBBytes, _ := common.HexToBytes("0xb64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d717")

	authA, _ := ed25519.NewPublicKey(authABytes)
	authB, _ := ed25519.NewPublicKey(authBBytes)

	expected := []types.Authority{
		{Key: authA, Weight: 1},
		{Key: authB, Weight: 1},
	}

	require.Equal(t, expected, auths)
}

func TestInstance_GrandpaAuthorities_PolkadotRuntime(t *testing.T) {
	tt := inmemory_trie.NewEmptyTrie()

	value, err := common.HexToBytes("0x0108eea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d714103640100000000000000b64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d7170100000000000000") //nolint:lll
	require.NoError(t, err)

	key := common.MustHexToBytes(genesis.GrandpaAuthoritiesKeyHex)
	tt.Put(key, value)

	rt := NewTestInstance(t, runtime.POLKADOT_RUNTIME_v0929, TestWithTrie(tt))

	auths, err := rt.GrandpaAuthorities()
	require.NoError(t, err)

	authABytes, _ := common.HexToBytes("0xeea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d71410364")
	authBBytes, _ := common.HexToBytes("0xb64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d717")

	authA, _ := ed25519.NewPublicKey(authABytes)
	authB, _ := ed25519.NewPublicKey(authBBytes)

	expected := []types.Authority{
		{Key: authA, Weight: 1},
		{Key: authB, Weight: 1},
	}

	require.Equal(t, expected, auths)
}

func TestInstance_BabeGenerateKeyOwnershipProof(t *testing.T) {
	testCases := []struct {
		name          string
		targetRuntime string
	}{
		{
			name:          "with_polkadot_runtime",
			targetRuntime: runtime.POLKADOT_RUNTIME_v0929,
		},
		{
			name:          "with_westend_runtime",
			targetRuntime: runtime.WESTEND_RUNTIME_v0929,
		},
	}
	for _, testCase := range testCases {
		testCase := testCase
		t.Run(testCase.name, func(t *testing.T) {
			tt := inmemory_trie.NewEmptyTrie()

			randomnessValue, err := common.HexToHash("0x01")
			require.NoError(t, err)
			key := common.MustHexToBytes(genesis.BABERandomnessKeyHex)
			tt.Put(key, randomnessValue[:])

			authorityValue, err := common.HexToBytes("0x08eea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d714103640100000000000000b64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d7170100000000000000") //nolint:lll
			require.NoError(t, err)

			key = common.MustHexToBytes(genesis.BABEAuthoritiesKeyHex)
			tt.Put(key, authorityValue)

			rt := NewTestInstance(t, testCase.targetRuntime, TestWithTrie(tt))

			babeConfig, err := rt.BabeConfiguration()
			require.NoError(t, err)

			require.NotEmpty(t, babeConfig.GenesisAuthorities)

			authorityID := babeConfig.GenesisAuthorities[0].Key

			const slot = uint64(10)
			res, err := rt.BabeGenerateKeyOwnershipProof(slot, authorityID)
			require.NoError(t, err)
			require.Nil(t, res)
		})
	}
}

func TestInstance_BabeSubmitReportEquivocationUnsignedExtrinsic(t *testing.T) {
	testCases := []struct {
		name          string
		targetRuntime string
	}{
		{
			name:          "with_polkadot_runtime",
			targetRuntime: runtime.POLKADOT_RUNTIME_v0929,
		},
		{
			name:          "with_westend_runtime",
			targetRuntime: runtime.WESTEND_RUNTIME_v0929,
		},
	}
	for _, testCase := range testCases {
		testCase := testCase
		t.Run(testCase.name, func(t *testing.T) {
			tt := inmemory_trie.NewEmptyTrie()
			rt := NewTestInstance(t, testCase.targetRuntime, TestWithTrie(tt))
			authorityID := types.AuthorityID{1}
			const slot = uint64(1)

			keyOwnershipProof := testKeyOwnershipProof

			equivocationProof := types.BabeEquivocationProof{
				Offender: authorityID,
				Slot:     slot,
			}

			err := rt.BabeSubmitReportEquivocationUnsignedExtrinsic(equivocationProof, keyOwnershipProof)
			require.NoError(t, err)
		})
	}
}

func TestInstance_BabeConfiguration_WestendRuntime_NoAuthorities(t *testing.T) {
	rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	cfg, err := rt.BabeConfiguration()
	require.NoError(t, err)

	expected := &types.BabeConfiguration{
		SlotDuration:       6000,
		EpochLength:        600,
		C1:                 1,
		C2:                 4,
		GenesisAuthorities: nil,
		Randomness:         [32]byte{},
		SecondarySlots:     2,
	}
	require.Equal(t, expected, cfg)
}

func TestInstance_BadSignature_WestendBlock8077850(t *testing.T) {
	tests := map[string]struct {
		setupRuntime  func(t *testing.T) (*Instance, *types.Header)
		expectedError []byte
	}{
		"westend_dev_runtime_should_fail_with_bad_signature": {
			expectedError: []byte{1, 0, 0xa},
			setupRuntime: func(t *testing.T) (*Instance, *types.Header) {
				genesisPath := utils.GetWestendDevRawGenesisPath(t)
				gen := genesisFromRawJSON(t, genesisPath)
				genTrie, err := runtime.NewTrieFromGenesis(gen)
				require.NoError(t, err)

				// set state to genesis state
				genState := storage.NewInMemoryTrieState(genTrie)

				cfg := Config{
					Storage: genState,
					LogLvl:  log.Critical,
				}

				rt, err := NewRuntimeFromGenesis(cfg)
				require.NoError(t, err)

				// reset state back to parent state before executing
				parentState := storage.NewInMemoryTrieState(genTrie)
				rt.SetContextStorage(parentState)

				genesisHeader := &types.Header{
					Number:    0,
					StateRoot: trie.V0.MustHash(genTrie), // Use right version from runtime
				}

				header := &types.Header{
					ParentHash: genesisHeader.Hash(),
					Number:     1,
					Digest:     types.NewDigest(),
				}

				return rt, header
			},
		},
		"westend_0912_runtime_should_fail_with_invalid_payment": {
			expectedError: []byte{1, 0, 1},
			setupRuntime: func(t *testing.T) (*Instance, *types.Header) {
				genesisPath := utils.GetWestendDevRawGenesisPath(t)
				gen := genesisFromRawJSON(t, genesisPath)
				genTrie, err := runtime.NewTrieFromGenesis(gen)
				require.NoError(t, err)

				rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0912)
				parentState := storage.NewInMemoryTrieState(genTrie)
				rt.SetContextStorage(parentState)

				genesisHeader := &types.Header{
					Number:    0,
					StateRoot: trie.V0.MustHash(genTrie), // Use right version from runtime
				}

				header := &types.Header{
					ParentHash: genesisHeader.Hash(),
					Number:     1,
					Digest:     types.NewDigest(),
				}

				return rt, header
			},
		},
	}

	for tname, tt := range tests {
		tt := tt

		t.Run(tname, func(t *testing.T) {
			instance, header := tt.setupRuntime(t)

			err := instance.InitializeBlock(header)
			require.NoError(t, err)

			idata := types.NewInherentData()
			err = idata.SetInherent(types.Timstap0, uint64(5))
			require.NoError(t, err)

			err = idata.SetInherent(types.Babeslot, uint64(1))
			require.NoError(t, err)

			ienc, err := idata.Encode()
			require.NoError(t, err)

			// Call BlockBuilder_inherent_extrinsics which returns the inherents as encoded extrinsics
			inherentExts, err := instance.InherentExtrinsics(ienc)
			require.NoError(t, err)

			// decode inherent extrinsics
			cp := make([]byte, len(inherentExts))
			copy(cp, inherentExts)
			var inExts [][]byte
			err = scale.Unmarshal(cp, &inExts)
			require.NoError(t, err)

			// apply each inherent extrinsic
			for _, inherent := range inExts {
				in, err := scale.Marshal(inherent)
				require.NoError(t, err)

				ret, err := instance.ApplyExtrinsic(in)
				require.NoError(t, err)
				require.Equal(t, ret, []byte{0, 0})
			}

			keyring, err := signature.KeyringPairFromSecret(
				"0x00000000000000000000000000000000000000000000000000000"+
					"00000000000000000000000000000000000000000000000000000"+
					"0000000000000000000000", 42)
			require.NoError(t, err)

			extHex := runtime.NewTestExtrinsic(t, instance, header.ParentHash, header.ParentHash,
				0, keyring, "System.remark", []byte{0xab, 0xcd})

			res, err := instance.ApplyExtrinsic(common.MustHexToBytes(extHex))
			require.NoError(t, err)

			// should fail with transaction validity error: invalid payment for runtime 0.9.12
			// should fail with transaction validity error: bad signature for runtime version greater than 0.9.12
			require.Equal(t, tt.expectedError, res)
		})
	}

}

func TestInstance_BabeConfiguration_WestendRuntime_WithAuthorities(t *testing.T) {
	tt := inmemory_trie.NewEmptyTrie()

	randomnessValue, err := common.HexToHash("0x01")
	require.NoError(t, err)
	key := common.MustHexToBytes(genesis.BABERandomnessKeyHex)
	tt.Put(key, randomnessValue[:])

	authorityValue, err := common.HexToBytes("0x08eea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d714103640100000000000000b64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d7170100000000000000") //nolint:lll
	require.NoError(t, err)

	key = common.MustHexToBytes(genesis.BABEAuthoritiesKeyHex)
	tt.Put(key, authorityValue)

	rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929, TestWithTrie(tt))

	cfg, err := rt.BabeConfiguration()
	require.NoError(t, err)

	authA, _ := common.HexToHash("0xeea1eabcac7d2c8a6459b7322cf997874482bfc3d2ec7a80888a3a7d71410364")
	authB, _ := common.HexToHash("0xb64994460e59b30364cad3c92e3df6052f9b0ebbb8f88460c194dc5794d6d717")

	expectedAuthData := []types.AuthorityRaw{
		{Key: authA, Weight: 1},
		{Key: authB, Weight: 1},
	}

	expected := &types.BabeConfiguration{
		SlotDuration:       6000,
		EpochLength:        600,
		C1:                 1,
		C2:                 4,
		GenesisAuthorities: expectedAuthData,
		Randomness:         [32]byte{1},
		SecondarySlots:     2,
	}

	require.Equal(t, expected, cfg)
}

func TestInstance_InitializeBlock_NodeRuntime(t *testing.T) {
	rt := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)

	header := &types.Header{
		Number: 1,
		Digest: types.NewDigest(),
	}

	err := rt.InitializeBlock(header)
	require.NoError(t, err)
}

func TestInstance_InitializeBlock_PolkadotRuntime(t *testing.T) {
	rt := NewTestInstance(t, runtime.POLKADOT_RUNTIME_v0929)

	header := &types.Header{
		Number: 1,
		Digest: types.NewDigest(),
	}

	err := rt.InitializeBlock(header)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_WestendRuntime(t *testing.T) {
	instance := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	block := runtime.InitializeRuntimeToTest(t, instance, &types.Header{})

	// reset state back to parent state before executing
	parentState := storage.NewInMemoryTrieState(inmemory_trie.NewEmptyTrie())
	instance.SetContextStorage(parentState)

	_, err := instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ApplyExtrinsic_WestendRuntime(t *testing.T) {
	genesisPath := utils.GetWestendDevRawGenesisPath(t)
	gen := genesisFromRawJSON(t, genesisPath)
	genTrie, err := runtime.NewTrieFromGenesis(gen)
	require.NoError(t, err)

	// set state to genesis state
	genState := storage.NewInMemoryTrieState(genTrie)

	cfg := Config{
		Storage: genState,
		LogLvl:  log.Critical,
	}

	instance, err := NewRuntimeFromGenesis(cfg)
	require.NoError(t, err)

	// reset state back to parent state before executing
	parentState := storage.NewInMemoryTrieState(genTrie)
	instance.SetContextStorage(parentState)

	genesisHeader := &types.Header{
		Number:    0,
		StateRoot: trie.V0.MustHash(genTrie), // Use right version from runtime
	}
	header := &types.Header{
		ParentHash: genesisHeader.Hash(),
		Number:     1,
		Digest:     types.NewDigest(),
	}

	err = instance.InitializeBlock(header)
	require.NoError(t, err)

	extHex := runtime.NewTestExtrinsic(t, instance, genesisHeader.Hash(), genesisHeader.Hash(),
		0, signature.TestKeyringPairAlice, "System.remark", []byte{0xab, 0xcd})

	res, err := instance.ApplyExtrinsic(common.MustHexToBytes(extHex))
	require.NoError(t, err)
	require.Equal(t, []byte{0, 0}, res)
}

func TestInstance_ExecuteBlock_PolkadotRuntime(t *testing.T) {
	DefaultTestLogLvl = 0

	instance := NewTestInstance(t, runtime.POLKADOT_RUNTIME_v0929)

	block := runtime.InitializeRuntimeToTest(t, instance, &types.Header{})

	// reset state back to parent state before executing
	parentState := storage.NewInMemoryTrieState(inmemory_trie.NewEmptyTrie())
	instance.SetContextStorage(parentState)

	_, err := instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_PolkadotRuntime_PolkadotBlock1(t *testing.T) {
	genesisPath := utils.GetPolkadotGenesisPath(t)
	gen := genesisFromRawJSON(t, genesisPath)
	genTrie, err := runtime.NewTrieFromGenesis(gen)
	require.NoError(t, err)

	expectedGenesisRoot := common.MustHexToHash("0x29d0d972cd27cbc511e9589fcb7a4506d5eb6a9e8df205f00472e5ab354a4e17")
	require.Equal(t, expectedGenesisRoot, trie.V0.MustHash(genTrie))

	// set state to genesis state
	genState := storage.NewInMemoryTrieState(genTrie)

	cfg := Config{
		Storage: genState,
		LogLvl:  log.Critical,
	}

	instance, err := NewRuntimeFromGenesis(cfg)
	require.NoError(t, err)

	// block data is received from querying a polkadot node
	body := []byte{8, 40, 4, 3, 0, 11, 80, 149, 160, 81, 114, 1, 16, 4, 20, 0, 0}
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 2, len(exts))

	// digest data received from querying polkadot node
	digestBytes := common.MustHexToBytes("0x0c0642414245b501010000000093decc0f00000000362ed8d6055645487fe42e9c8640be651f70a3a2a03658046b2b43f021665704501af9b1ca6e974c257e3d26609b5f68b5b0a1da53f7f252bbe5d94948c39705c98ffa4b869dd44ac29528e3723d619cc7edf1d3f7b7a57a957f6a7e9bdb270a044241424549040118fa3437b10f6e7af8f31362df3a179b991a8c56313d1bcd6307a4d0c734c1ae310100000000000000d2419bc8835493ac89eb09d5985281f5dff4bc6c7a7ea988fd23af05f301580a0100000000000000ccb6bef60defc30724545d57440394ed1c71ea7ee6d880ed0e79871a05b5e40601000000000000005e67b64cf07d4d258a47df63835121423551712844f5b67de68e36bb9a21e12701000000000000006236877b05370265640c133fec07e64d7ca823db1dc56f2d3584b3d7c0f1615801000000000000006c52d02d95c30aa567fda284acf25025ca7470f0b0c516ddf94475a1807c4d250100000000000000000000000000000000000000000000000000000000000000000000000000000005424142450101d468680c844b19194d4dfbdc6697a35bf2b494bda2c5a6961d4d4eacfbf74574379ba0d97b5bb650c2e8670a63791a727943bcb699dc7a228bdb9e0a98c9d089") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)

	// polkadot block 1, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"),
			Number:         1,
			StateRoot:      common.MustHexToHash("0xc56fcd6e7a757926ace3e1ecff9b4010fc78b90d459202a339266a7f6360002f"),
			ExtrinsicsRoot: common.MustHexToHash("0x9a87f6af64ef97aff2d31bebfdd59f8fe2ef6019278b634b2515a38f1c4c2420"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock1(t *testing.T) {
	genesisPath := utils.GetKusamaGenesisPath(t)
	gen := genesisFromRawJSON(t, genesisPath)
	genTrie, err := runtime.NewTrieFromGenesis(gen)
	require.NoError(t, err)

	expectedGenesisRoot := common.MustHexToHash("0xb0006203c3a6e6bd2c6a17b1d4ae8ca49a31da0f4579da950b127774b44aef6b")
	require.Equal(t, expectedGenesisRoot, trie.V0.MustHash(genTrie))

	// set state to genesis state
	genState := storage.NewInMemoryTrieState(genTrie)

	cfg := Config{
		Storage: genState,
		LogLvl:  log.Critical,
	}

	instance, err := NewRuntimeFromGenesis(cfg)
	require.NoError(t, err)

	// block data is received from querying a polkadot node
	body := []byte{8, 40, 4, 2, 0, 11, 144, 17, 14, 179, 110, 1, 16, 4, 20, 0, 0}
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 2, len(exts))

	// digest from polkadot.js
	digestBytes := common.MustHexToBytes("0x0c0642414245340201000000ef55a50f00000000044241424549040118ca239392960473fe1bc65f94ee27d890a49c1b200c006ff5dcc525330ecc16770100000000000000b46f01874ce7abbb5220e8fd89bede0adad14c73039d91e28e881823433e723f0100000000000000d684d9176d6eb69887540c9a89fa6097adea82fc4b0ff26d1062b488f352e179010000000000000068195a71bdde49117a616424bdc60a1733e96acb1da5aeab5d268cf2a572e94101000000000000001a0575ef4ae24bdfd31f4cb5bd61239ae67c12d4e64ae51ac756044aa6ad8200010000000000000018168f2aad0081a25728961ee00627cfe35e39833c805016632bf7c14da5800901000000000000000000000000000000000000000000000000000000000000000000000000000000054241424501014625284883e564bc1e4063f5ea2b49846cdddaa3761d04f543b698c1c3ee935c40d25b869247c36c6b8a8cbbd7bb2768f560ab7c276df3c62df357a7e3b1ec8d") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)

	// kusama block 1, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"),
			Number:         1,
			StateRoot:      common.MustHexToHash("0xfabb0c6e92d29e8bb2167f3c6fb0ddeb956a4278a3cf853661af74a076fc9cb7"),
			ExtrinsicsRoot: common.MustHexToHash("0xa35fb7f7616f5c979d48222b3d2fa7cb2331ef73954726714d91ca945cc34fd8"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock3784(t *testing.T) {
	gossTrie3783 := newTrieFromPairs(t, "../test_data/kusama/block3783.out")
	expectedRoot := common.MustHexToHash("0x948338bc0976aee78879d559a1f42385407e5a481b05a91d2a9386aa7507e7a0")
	require.Equal(t, expectedRoot, trie.V0.MustHash(gossTrie3783))

	// set state to genesis state
	state3783 := storage.NewInMemoryTrieState(gossTrie3783)

	cfg := Config{
		Storage: state3783,
		LogLvl:  log.Critical,
	}

	instance, err := NewInstanceFromTrie(gossTrie3783, cfg)
	require.NoError(t, err)

	// block data is received from querying a polkadot node
	body := common.MustHexToBytes("0x10280402000bb00d69b46e0114040900193b10041400009101041300eaaec5728cd6ea9160ff92a49bb45972c532d2163241746134726aaa5b2f72129d8650715320f23765c6306503669f69bf684b188dea73b1e247dd1dd166513b1c13daa387c35f24ac918d2fa772b73cffd20204a8875e48a1b11bb3229deb7f00") //nolint:lll
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 4, len(exts))

	// digest from polkadot.js
	digestBytes := common.MustHexToBytes("0x080642414245340203000000bd64a50f0000000005424142450101bc0d6850dba8d32ea1dbe26cb4ac56da6cca662c7cc642dc8eed32d2bddd65029f0721436eafeebdf9b4f17d1673c6bc6c3c51fe3dda3121a5fc60c657a5808b") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)

	// kusama block 3784, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0x4843b4aa38cf2e3e2f6fae401b98dd705bed668a82dd3751dc38f1601c814ca8"),
			Number:         3784,
			StateRoot:      common.MustHexToHash("0xac44cc18ec22f0f3fca39dfe8725c0383af1c982a833e081fbb2540e46eb09a5"),
			ExtrinsicsRoot: common.MustHexToHash("0x52b7d4852fc648cb8f908901e1e36269593c25050c31718454bca74b69115d12"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock901442(t *testing.T) {
	ksmTrie901441 := newTrieFromPairs(t, "../test_data/kusama/block901441.out")
	expectedRoot := common.MustHexToHash("0x3a2ef7ee032f5810160bb8f3ffe3e3377bb6f2769ee9f79a5425973347acd504")
	require.Equal(t, expectedRoot, trie.V0.MustHash(ksmTrie901441))

	// set state to genesis state
	state901441 := storage.NewInMemoryTrieState(ksmTrie901441)

	cfg := Config{
		Storage: state901441,
		LogLvl:  log.Critical,
	}

	instance, err := NewInstanceFromTrie(ksmTrie901441, cfg)
	require.NoError(t, err)

	body := common.MustHexToBytes("0x0c280402000b207eb80a70011c040900fa0437001004140000")
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 3, len(exts))

	// digest from polkadot.js
	digestBytes := common.MustHexToBytes("0x080642414245340244000000aeffb30f00000000054241424501011cbef2a084a774c34d9990c7bfc6b4d2d5e9f5b59feca792cd2bb89a890c2a6f09668b5e8224879f007f49f299d25fbb3c0f30d94fb8055e07fa8a4ed10f8083") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)
	require.Equal(t, 2, len(digest))

	// kusama block 901442, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0x68d9c5f75225f09d7ce493eff8aabac7bae8b65cb81a2fd532a99fbb8c663931"),
			Number:         901442,
			StateRoot:      common.MustHexToHash("0x6ea065f850894c5b58cb1a73ec887e56842851943641149c57cea357cae4f596"),
			ExtrinsicsRoot: common.MustHexToHash("0x13483a4c148fff5f072e86b5af52bf031556514e9c87ea19f9e31e7b13c0c414"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock1377831(t *testing.T) {
	ksmTrie := newTrieFromPairs(t, "../test_data/kusama/block1377830.out")
	expectedRoot := common.MustHexToHash("0xe4de6fecda9e9e35f937d159665cf984bc1a68048b6c78912de0aeb6bd7f7e99")
	require.Equal(t, expectedRoot, trie.V0.MustHash(ksmTrie))

	// set state to genesis state
	state := storage.NewInMemoryTrieState(ksmTrie)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Critical,
	}

	instance, err := NewInstanceFromTrie(ksmTrie, cfg)
	require.NoError(t, err)

	body := common.MustHexToBytes("0x08280402000b60c241c070011004140000")
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 2, len(exts))

	// digest from polkadot.js
	digestBytes := common.MustHexToBytes("0x080642414245b50101020000008abebb0f00000000045553c32a949242580161bcc35d7c3e492e66defdcf4525d7a338039590012f42660acabf1952a2d5d01725601705404d6ac671507a6aa2cf09840afbdfbb006f48062dae16c56b8dc5c6ea6ffba854b7e8f46e153e98c238cbe7bbb1556f0b0542414245010136914c6832dd5ba811a975a3b654d76a1ec81684f4b03d115ce2e694feadc96411930438fde4beb008c5f8e26cfa2f5b554fa3814b5b73d31f348446fd4fd688") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)
	require.Equal(t, 2, len(digest))

	// kusama block 1377831, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0xca387b3cc045e8848277069d8794cbf077b08218c0b55f74d81dd750b14e768c"),
			Number:         1377831,
			StateRoot:      common.MustHexToHash("0x7e5569e652c4b1a3cecfcf5e5e64a97fe55071d34bab51e25626ec20cae05a02"),
			ExtrinsicsRoot: common.MustHexToHash("0x7f3ea0ed63b4053d9b75e7ee3e5b3f6ce916e8f59b7b6c5e966b7a56ea0a563a"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_KusamaRuntime_KusamaBlock1482003(t *testing.T) {
	ksmTrie := newTrieFromPairs(t, "../test_data/kusama/block1482002.out")
	expectedRoot := common.MustHexToHash("0x09f9ca28df0560c2291aa16b56e15e07d1e1927088f51356d522722aa90ca7cb")
	require.Equal(t, expectedRoot, trie.V0.MustHash(ksmTrie))

	// set state to genesis state
	state := storage.NewInMemoryTrieState(ksmTrie)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Critical,
	}

	instance, err := NewInstanceFromTrie(ksmTrie, cfg)
	require.NoError(t, err)

	body := common.MustHexToBytes("0x0c280402000b10c3e3e570011c04090042745a001004140000")
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 3, len(exts))

	// digest from polkadot.js
	digestBytes := testdata.DigestKusama1482002(t)

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)

	require.Equal(t, 4, len(digest))

	// kusama block 1482003, from polkadot.js
	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0x587f6da1bfa71a675f10dfa0f63edfcf168e8ece97eb5f526aaf0e8a8e82db3f"),
			Number:         1482003,
			StateRoot:      common.MustHexToHash("0xd2de750002f33968437bdd54912dd4f55c3bddc5a391a8e0b8332568e1efea8d"),
			ExtrinsicsRoot: common.MustHexToHash("0xdf5da95780b77e83ad0bf820d5838f07a0d5131aa95a75f8dfbd01fbccb300bd"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_ExecuteBlock_PaseoRuntime_PaseoBlock1008649(t *testing.T) {
	paseoTrie := newTrieFromKeyValueList(t, "../test_data/paseo/block1008648.out")
	expectedRoot := common.MustHexToHash("0x4fbffb5810aca70ff9e3d6560f496ca3b3beef098ab76d18e48283ec7c8ffa9b")
	require.Equal(t, expectedRoot, trie.V0.MustHash(paseoTrie))

	// set state to genesis state
	state := storage.NewInMemoryTrieState(paseoTrie)

	db, err := database.NewPebble("", true)
	require.NoError(t, err)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Critical,
		NodeStorage: runtime.NodeStorage{
			LocalStorage:      db,
			PersistentStorage: db,
			BaseDB:            db,
		},
	}

	instance, err := NewInstanceFromTrie(paseoTrie, cfg)
	require.NoError(t, err)

	blockResponse := &messages.BlockResponseMessage{}
	err = blockResponse.Decode(common.MustHexToBytes("0x0ab81d0a202dcc0a3c3d4953da49e0006da24c7617a55e2e169dcf59aacc5fbd5856b14fb312c702c154fd69bab5b5676a746c32868b053980a53bb0aaae89fc47d5408056883eb826903d00c75b6a15438acb997f925a09714092fc463af3ba44ab93654c89b775c44dfe13dd89f50472e232a4cff46ce80cfa07fa8777a7acfc2c40a52e7f23b481a2a1b60c0642414245b501030c000000e02c081100000000a4acb383795247df8efcf162903c35af1d09774e83def72e149382695048073868402c77fbb9b7c54c842910999327853c735df23083fd26bd3d2a4a3c934607a875840cddf647d6ab39189d11b07a0e423645e80e0c681ebffc54926a2d0f0a0442454546840318e81c2a62d4f79164052d2969cff6ddfcc35c6fd5bbac6bd5af7e66c64d7d32054241424501017a1cf0a7fc757c3e6cebf7b937ec6bceb42239be3cab62c2a6fec237d00f380a73bcc611b3b07533378e79e47a8b9553f438763e39358f45b09247bbf949f1881a0b280403000b00c29b2f8f011abc1ae934043600ac24e5010000000042f2d3629bcee331ba3e797da9bc8f7b114c56cf5879916f0a5ccaacbbcfcd33c0d66483f41f0aee1c18722313e9e634bd6f443dd420c63e7c8c0fa02a2a4e8824e70101000000d63256338b66d3d86dce4341858efc26160dc11f40885d7ba574b2cbdbb05e3cb80d46137b91740b84a8989e792c276101d6821b8258b50c28f53209db9d588424e50102000000d886d2c0a6a7b7d91d31cd79d6395a9314ce484ef1aab5a77a91683ba0611919551c2dbc369ba092e28b84d2d54e88ae2dd060fec60a9c0051adba0233df348224e70103000000aa4acbcf105d03e73ab393aa6227c5bba1c65c880be240b98175c21d71934d0e6b2763ec6af35d403764a56a4ce6cad623978141cd065fb9f89f3c4e2913fd8c24e70104000000ec43f63380097da3ffd0453684f3da6a3c26d6dd09ec76c06ec16d109f0c2b3b7d78226c2ef6279ffaa1bba644a82575830156804e425a36817e16bcc453888024e50105000000a63a6b5385a3b5991f259872608e5adbe41aa41835c72c200457cb61919d393c222e41187cda30be8d7f8661ea9ae1d6310b2f19bcbda51815bae6b12b088b8c24e501060000006a80b176d88acb02140385f788e128e4a67f97848074dde691d674d8e8cb191d1b2994d37d17d4bee2bcc523832b98fc3f4227e1ddc54cf4dcfdd41ade07e38824e50107000000020532162b48a2be92028352a84523d07036e767430e1dde4aec92efa64e155e74f7fbbb92ec95db26f115950a877efab4a1a30df074cf59c7cc244d950f1b8e24e501080000006c1864b2125754ffddfe8d6f60df4f4f342fda159a9b7ac407b34dfd17c9a532287ff9011bd9d587d98bb0543bb8b92ea854f1385672c76c733c54cea0bb498924e50109000000ec340ccf10147706d756441b1648ddb8b94b119e10f2996682c27d612df5d547d7506c11490121ae784f9306655425b6e81f4cba0fb8e43b2892b2cf4588a68f24e5010a000000f4d3ecd0f7c4b61d01ecbeb4bb06df50e483d57dfd7eeced80baf6d8ce9d2b0c594457e8b2c29a55558091bdb5632b535def00b9d3171b77ce4348037866b08724e7010b000000ce2cba80c37ae22e7b18bad4955949ab3bf897a56b10962c03cd13374a09f15342b14e8f9f89610326f4591ad8cd85d26b31128f8a5fb734991242ecb24f728824e5010c000000ec431a72f9684c632c21eba1798c25a7f50a20323a82288e3b8b70fcec65941acc3dcf8794966948aa288929f2857d1c6bb6ace040d76428f1aeed86992c688124e5010d000000383d447003f376f260094e480901c952f9e6f7898963f0ea9f2c182bcf970b38807310db0bb96bda55bbf2aa308e39c40f9216a01661ee7f285143ccf9692f8824e5010e0000007a7d6c9dca3c7fa7ae893defbccd66b4667c6bbfc343c9d28a82107df2b4774ecd1825a31f4ebd10c1ef99be9bd3a2ea1e82c9d5498ed9d74cf48dffe388b98324e7010f00000024aa634d5fe7eeee763b99bd866ebebe200276324725e0fd4805ef8c8ea12e0dfd31821e04853e387ad5e0d0784dbb8fceabda017c18708cad4b1deacd39a58b24e701100000006edb6f3c9d893bbb12ef1d96cbb6b2c0056c85efcc2209b946a6493c875b671db57fa876570554e9e2125aaddfb78329c5904c3a395f50c93172cb6439529c8424e501110000004a0a3ba9408d28223b760c0c45858d07c40a73b9d6d571ab556c03687404c330c812e899eb55aaafab0f433caa8b1672a654aad12547f515096362b62b01ed8b24e501120000003cb4aebfd1885088d9b8fa5fffc03d7c0534ad854be79c4b79c312c64f8c5b57ca31bcb5d187074357c1141b860d76a6b8265234b48cb1f35fbcb3907bcb6b8b24e701130000006caab829a264c4111a3d5c25adf7a453beaf10ec1808e9e56781666fb4a8947c3dddebe5546c2659136d9b54cb7effb86e7fb674c6ec6ea1b0be58e424426a8e24e701140000006acbb3e3747dd3cc8da8b799825ac9acdda50f06a50683a78d98fa0974fc0c7d9466a23685dfb7e4beaa5f3d3ca823d59a418f69a576df97dc3daf7eb1186c8624e70115000000f4676fdc3ef1fd8e9335ee39037e259bfe47d3c44c281f1e65e24c8754f99433735a16bbf461f1caddefb2936217785db25f41ef0d2434e18ee24025a315058224e70116000000d00612c21d6b56d069bba06f93c4abba858ee451b77c54c97be96b425a82eb31813e55c7dbca3194189ade96148964a90c2da6a3c8bc6790bbaaa4e6e351098824e701170000009c6b8cc879a2392e7d355c1fe36e7e935ca34bdef19c8260cb0a05585ed13b6ea02c8a8309807dbdaeccc722d4857000f9d0dd924d02562a7ab116ea34fe4b8a24e50118000000e2cab7cd19724ca5dd6d8451e2029422b7182ab865a41a7424b92058602cac45fe1b3b683cd904f66bb3a30f0cda65c28848dffbb861b4734e6195a25d15ba8724e70119000000ccb1fc1afac473206a141c1ae6b396009b1c9b18e958dbd2ff3ecdcf49de800a2b49d32e21f4b48ee612bea0e2c79a4033832bfbac8301e47b7466d0ce8d6c8724e7011a000000c29e7059a38a50559fc8b788eb39b4222427fc8505bce5e9ec909b32317801015e6603dc610cd4980d53682b44954dc882ec1b2410e6ee62396f08f320b3468c24e7011b00000028ecbd92be9f51dcfeaea071e13972e510b78dfecdc378e381be8ffb9ff0697e3b8487a9aa611d251cfc8135b4bcfe97c62870a887ec0532356b869953020a8e24e5011c000000623b8905d098e1be6b3e8a27f7c9d166d57276981447829f76b827a3ce092e560f459ace810b0e5fb56b67c3081afd36fc30d4d1f5a01a3319f6333ea7593e8e24e5011d00000074a390dec4d88662d690e7ac19e109c6bfc05d48462918e60f31f07415925d4987e9760e07e29b726365ccacb5ddbb226e8817b85b298d39c84c79db67df6c8124e7011e00000046111b603d728bd7ccc2d9f61268c0b24b7f709001fd2dc5389a74797021b240d2c71165f106fc0168be101c05f594d73b7f621bbf201c514d042ead4477b28624e7011f000000d0c54ec78326b880088b86fc2be9f189299d1a8a7b2ea7ec08d5ef7c57ba83074f1342900dc808b7f63223d1274d018e1c5ca2c1ec1a5d0402c6d0a8f7f3148324e70120000000947bb9cc6c1ed2c88ba722fcc863e4e5858adefe47eead172883ba23b46e4d031c1e0ccf6ee8417d93e1ec4897c34efc4a5f4f5e5a0858e7176c284a0f915e8c24e5012100000026637a5c715a6b9811b6fe0ed40ad9c682a67c6331c4ce64fb3433c976b80c3365aa283ba85e5789a010617da58b28d52378bde135506250fde6b35f2a6efe8724e7012200000084c8c3bc09469d8ce5ff553cdaeea7d5872c5b8ba2f0e60f17e8e072a2545e5ecda680e8c268284def421a166b1b3523450e69206662891c0dbf11c9443e0c8b24e70123000000c044e668ce3522a731adaecce85ebfcbe529801f8ce1a4076ddf27e0cf732d6ace24d0d4375bb3d3516069a6e347e33ea81fad337928f7f06d03bacb1c08688624e7012400000080f81db560c9ea46e6659f7f539c73e1be80e635f5fc79854525f6f6883c600198447d9b235746003dfcf0f2b683a377b91eeb4343b992c487796e846b727f8324e701250000009008cfae49b4a3157c30bbff6e32e1efa473d71bb6d101fb1e0edcde618e3063b07a0aedd3f7624fd71cec63f81de3d0d837f9d72edf6282cf6e234875dda18024e701260000007806a8eade36a4f7bb824a8ed3ee19e64411caf315fbcdeb4c709afaf81a106d504e713b4356c7dff25d00d07f65c7847c12e9843876c24033a5d23e6c40c480240200270000008a9428f41d3fdd6413602758a5b8658e4f40a2b26fa296ef7c38514b305f337aeca2686aff49e656c2f3dd676a9b2c0b5ff65c5101ab8209ed18e4d9c2d7348b24e50128000000d8e58bf175f49e98da8e49d3dfa1539fbb1a0cea98c47a40d949095385504623e20a60aeef90fce21e6428e48b481cf452b4e47acc1d7bd9d42ffdb885133e8924e50129000000ce093b859e143eb44826888c17c6dc8456543668b0637958ae6f962d1638a02093039cff3950172663f645f6c1db5e503579cc0905c4e924c6a35fbb26a0d08b24e7012a0000005e31978a3e353144f41818892d2b664d9fc2454d4b9db3923281228ceee87c453d9b3239488681c038e60356a30d8d558c0254561f3843c5b492aabc6623bc870000bc2924e71e51b4ebc3657e523f90ce68cce6d0d56cb50ff350e9e3a40762c9e822903d004fbffb5810aca70ff9e3d6560f496ca3b3beef098ab76d18e48283ec7c8ffa9b340eeeb98f740c3ab0241a51890e0e178ebc5633dd547ab1e7c89130a269507a0c0642414245b5010319000000df2c0811000000005ef4b3ef782dcf55f6908627668e7765b501877fc237cffe5b7fa4e7b1f16323662e712d38fe8b4cde838be86103551014cd55fcc201a580836de920e2afaa0b095f61f4b7a25e7a0b0f78f437b97ab00117c6baa0abccd353a6260be0637a030442454546840388660a70d906e4ff831f89296abeb9efa4bfc4b794a87c4ca2ecf65b6f52b97805424142450101d4748ed493c413ccfe6c4a08ef0dfbab6bb763ceeb6d552e72213fefe0ee9538ce2bd597a112c32b88d908ac07e38951996033344989bd8808d2406e9454318d")) //nolint:lll
	require.NoError(t, err)

	block := &types.Block{
		Header: *blockResponse.BlockData[0].Header,
		Body:   *blockResponse.BlockData[0].Body,
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)

	expectedRootNew := common.MustHexToHash("0xc75b6a15438acb997f925a09714092fc463af3ba44ab93654c89b775c44dfe13")

	root, err := state.Root()
	require.NoError(t, err)

	require.Equal(t, expectedRootNew, root)
}

func TestInstance_ExecuteBlock_PolkadotBlock1089328(t *testing.T) {
	dotTrie := newTrieFromPairs(t, "../test_data/polkadot/block1089327.json")
	expectedRoot := common.MustHexToHash("0x87ed9ebe7fb645d3b5b0255cc16e78ed022d9fbb52486105436e15a74557535b")
	require.Equal(t, expectedRoot, trie.V0.MustHash(dotTrie))

	// set state to genesis state
	state := storage.NewInMemoryTrieState(dotTrie)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Critical,
	}

	instance, err := NewInstanceFromTrie(dotTrie, cfg)
	require.NoError(t, err)

	body := common.MustHexToBytes("0x0c280403000be02ab6d873011004140000b90384468e34dbdcc8da24e44b0f0d34d97ccad5ce0281e465db0cc1d8e1423d50d90a018a89185c693f77b050fa35d1f80b19608b72a6e626110e835caedf949668a12b0ad7b786accf2caac0ec874941ccea9825d50b6bb5870e1400f0e56bb4c18b87a5021501001d00862e432e0cf75693899c62691ac0f48967f815add97ae85659dcde8332708551001b000cf4da8aea0e5649a8bedbc1f08e8a8c0febe50cd5b1c9ce0da2164f19aef40f01014a87a7d3673e5c80aec79973682140828a0d1c3899f4f3cc953bd02673e11a022aaa4f269e3f1a90156db29df88f780b1540b610aeb5cd347ee703c5dff48485") //nolint:lll
	var exts [][]byte
	err = scale.Unmarshal(body, &exts)
	require.NoError(t, err)
	require.Equal(t, 3, len(exts))

	// digest from polkadot.js
	digestBytes := common.MustHexToBytes("0x080642414245b501017b000000428edd0f00000000c4fd75c7535d8eec375d70d21cc62262247b599aa67d8a9cf2f7d1b8cb93cd1f9539f04902c33d4c0fe47f723dfed8505d31de1c04d0036a9df233ff902fce0d70060908faa4b3f481e54cbd6a52dfc20c3faac82f746d84dc03c2f824a89a0d0542414245010122041949669a56c8f11b3e3e7c803e477ad24a71ed887bc81c956b59ea8f2b30122e6042494aab60a75e0db8fdff45951e456e6053bd64eb5722600e4a13038b") //nolint:lll

	digest := types.NewDigest()
	err = scale.Unmarshal(digestBytes, &digest)
	require.NoError(t, err)
	require.Equal(t, 2, len(digest))

	block := &types.Block{
		Header: types.Header{
			ParentHash:     common.MustHexToHash("0x21dc35454805411be396debf3e1d5aad8d6e9d0d7679cce0cc632ba8a647d07c"),
			Number:         1089328,
			StateRoot:      common.MustHexToHash("0x257b1a7f6bc0287fcbf50676dd29817f2f7ae193cb65b31962e351917406fa23"),
			ExtrinsicsRoot: common.MustHexToHash("0x950173af1d9fdcd0be5428fc3eaf05d5f34376bd3882d9a61b348fa2dc641012"),
			Digest:         digest,
		},
		Body: *types.NewBody(types.BytesArrayToExtrinsics(exts)),
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)
}

func TestInstance_DecodeSessionKeys(t *testing.T) {
	keys := "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d34309a9d2a24213896ff06895db16aade8b6502f3a71cf56374cc3852042602634309a9d2a24213896ff06895db16aade8b6502f3a71cf56374cc3852042602634309a9d2a24213896ff06895db16aade8b6502f3a71cf56374cc3852042602634309a9d2a24213896ff06895db16aade8b6502f3a71cf56374cc3852042602634309a9d2a24213896ff06895db16aade8b6502f3a71cf56374cc38520426026" //nolint:lll
	pubkeys, err := common.HexToBytes(keys)
	require.NoError(t, err)

	pukeysBytes, err := scale.Marshal(pubkeys)
	require.NoError(t, err)

	instance := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	decoded, err := instance.DecodeSessionKeys(pukeysBytes)
	require.NoError(t, err)

	var decodedKeys *[]struct {
		Data []uint8
		Type [4]uint8
	}

	err = scale.Unmarshal(decoded, &decodedKeys)
	require.NoError(t, err)

	require.NotNil(t, decodedKeys)
	require.Len(t, *decodedKeys, 6)
}

func TestInstance_PaymentQueryInfo(t *testing.T) {
	tests := []struct {
		extB       []byte
		ext        string
		errMessage string
		expect     *types.RuntimeDispatchInfo
	}{
		{
			// Was made with @polkadot/api on https://github.com/danforbes/polkadot-js-scripts/tree/create-signed-tx
			ext: "0xd1018400d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01bc2b6e35929aabd5b8bc4e5b0168c9bee59e2bb9d6098769f6683ecf73e44c776652d947a270d59f3d37eb9f9c8c17ec1b4cc473f2f9928ffdeef0f3abd43e85d502000000012844616e20466f72626573", //nolint:lll
			expect: &types.RuntimeDispatchInfo{
				Weight: 0,
				Class:  0,
				PartialFee: &scale.Uint128{
					Upper: 0,
					Lower: uint64(12800000000),
				},
			},
		},
		{
			// incomplete extrinsic
			ext:        "0x4ccde39a5684e7a56da23b22d4d9fbadb023baa19c56495432884d0640000000000000000000000000000000",
			errMessage: "running runtime function: wasm error",
		},
		{
			// incomplete extrinsic
			extB:       nil,
			errMessage: "running runtime function: wasm error",
		},
	}

	for _, test := range tests {
		var err error
		var extBytes []byte

		if test.ext == "" {
			extBytes = test.extB
		} else {
			extBytes, err = common.HexToBytes(test.ext)
			require.NoError(t, err)
		}

		ins := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
		info, err := ins.PaymentQueryInfo(extBytes)

		if test.errMessage != "" {
			assert.ErrorContains(t, err, test.errMessage)
			continue
		}
		require.NoError(t, err)

		require.NoError(t, err)
		require.NotNil(t, info)
		require.Equal(t, test.expect, info)
	}
}

func newTrieFromPairs(t *testing.T, filename string) trie.Trie {
	data, err := os.ReadFile(filename)
	require.NoError(t, err)

	rpcPairs := make(map[string]interface{})
	err = json.Unmarshal(data, &rpcPairs)
	require.NoError(t, err)
	pairs := rpcPairs["result"].([]interface{})

	entries := make(map[string]string)
	for _, pair := range pairs {
		pairArr := pair.([]interface{})
		entries[pairArr[0].(string)] = pairArr[1].(string)
	}

	tr, err := inmemory_trie.LoadFromMap(entries, trie.V0)
	require.NoError(t, err)
	return tr
}

func TestInstance_TransactionPaymentCallApi_QueryCallInfo(t *testing.T) {
	ins := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	tests := []struct {
		callHex    string
		errMessage string
		expect     *types.RuntimeDispatchInfo
	}{
		{
			// call generated by using palkadot.js/api v9.5.1: api.tx.system.remark("Ed")
			// and removing first byte (encoding) and second byte (unknown)
			callHex: "0x0001084564",
			expect: &types.RuntimeDispatchInfo{
				Weight: 0,
				Class:  0,
				PartialFee: &scale.Uint128{
					Upper: 0,
					Lower: uint64(1500000000),
				},
			},
		},
		{
			// call removing encoding (first byte), polkadot.js/api v9.5.1: api.tx.system.remark("Ed")
			// polkadot.js/api returns error:  RPC-CORE: call(method: Text, data: Bytes, at?: BlockHash):
			// Bytes:: -32000: Client error: Execution failed: Execution aborted due to trap: wasm trap: wasm
			// `unreachable` instruction executed
			callHex:    "0x040001084564",
			errMessage: "running runtime function: wasm error",
		},
		{
			// call without removing any bytes, polkadot.js/api v9.5.1: api.tx.system.remark("Ed test")
			// polkadot.js/api returns error: Error: createType(Call):: findMetaCall: Unable to find Call with index
			// [44, 4]/[44,4]
			callHex:    "0x2c0400011c45642074657374",
			errMessage: "running runtime function: wasm error",
		},
	}

	for _, test := range tests {
		var err error
		var callBytes []byte

		callBytes, err = common.HexToBytes(test.callHex)
		require.NoError(t, err)

		info, err := ins.QueryCallInfo(callBytes)

		if test.errMessage != "" {
			assert.ErrorContains(t, err, test.errMessage)
			continue
		}

		require.NoError(t, err)
		require.NotNil(t, info)
		require.Equal(t, test.expect, info)
	}
}

func TestInstance_TransactionPaymentCallApi_QueryCallFeeDetails(t *testing.T) {
	ins := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	tests := []struct {
		callHex    string
		errMessage string
		expect     *types.FeeDetails
	}{
		{
			// call generated by using palkadot.js/api v9.5.1: api.tx.system.remark("Ed")
			// and removing first byte (encoding) and second byte (unknown)
			callHex: "0x0001084564",
			expect: &types.FeeDetails{
				InclusionFee: types.InclusionFee{
					BaseFee: &scale.Uint128{
						Upper: 0,
						Lower: uint64(256000000001),
					},
					LenFee: &scale.Uint128{
						Upper: 0,
						Lower: uint64(128000000000),
					},
					AdjustedWeightFee: &scale.Uint128{},
				},
				Tip: &scale.Uint128{},
			},
		},
		{
			// call removing encoding (first byte), polkadot.js/api v9.5.1: api.tx.system.remark("Ed")
			// when calling polkadot node (v0.9.29) with polkadot.js/api the node returns error:  RPC-CORE: call(
			// method: Text, data: Bytes, at?: BlockHash): Bytes:: -32000: Client error: Execution failed:
			// Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed
			callHex:    "0x040001084564",
			errMessage: "running runtime function: wasm error",
		},
		{
			// call without removing any bytes, polkadot.js/api v9.5.1: api.tx.system.remark("Ed test")
			// when calling polkadot (v0.9.29) with polkadot.js/api the node returns error: Error: createType(
			// Call):: findMetaCall: Unable to find Call with index [44, 4]/[44,4]
			callHex:    "0x18040001084564",
			errMessage: "running runtime function: wasm error",
		},
	}

	for _, test := range tests {
		extBytes, err := common.HexToBytes(test.callHex)
		require.NoError(t, err)

		details, err := ins.QueryCallFeeDetails(extBytes)

		if test.errMessage != "" {
			assert.ErrorContains(t, err, test.errMessage)
			continue
		}

		require.NoError(t, err)
		require.NotNil(t, details)
		require.Equal(t, test.expect, details)
	}
}

func TestInstance_GrandpaGenerateKeyOwnershipProof(t *testing.T) {
	instance := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)
	identity := common.MustHexToBytes("0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee")
	identityPubKey, _ := ed25519.NewPublicKey(identity)
	authorityID := identityPubKey.AsBytes()

	opaqueKeyOwnershipProof, err := instance.GrandpaGenerateKeyOwnershipProof(uint64(0), authorityID)
	// Since the input is not valid with respect to the instance, an empty proof is returned
	require.NoError(t, err)
	require.Nil(t, opaqueKeyOwnershipProof)
}

func TestInstance_GrandpaSubmitReportEquivocationUnsignedExtrinsic(t *testing.T) {
	identity := common.MustHexToBytes("0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee")
	identityPubKey, _ := ed25519.NewPublicKey(identity)
	runtime := NewTestInstance(t, runtime.WESTEND_RUNTIME_v0929)

	keyOwnershipProofRaw := types.GrandpaOpaqueKeyOwnershipProof([]byte{64, 138, 252, 29, 127, 102, 189, 129, 207, 47,
		157, 60, 17, 138, 194, 121, 139, 92, 176, 175, 224, 16, 185, 93, 175, 251, 224, 81, 209, 61, 0, 71})
	keyOwnershipProof := scale.MustMarshal(keyOwnershipProofRaw)

	var opaqueKeyOwnershipProof types.GrandpaOpaqueKeyOwnershipProof
	err := scale.Unmarshal(keyOwnershipProof, &opaqueKeyOwnershipProof)
	require.NoError(t, err)

	firstVote := types.GrandpaVote{
		Hash:   common.MustHexToHash("0x4801b8e62d31167d30c893cc1970f6a0e289420282a4b245b75f2c46fb308af1"),
		Number: 10,
	}
	secondVote := types.GrandpaVote{
		Hash:   common.MustHexToHash("0xc314327941fdd924bc67fd72651c40aececd485ca3e878c21e02abb40feae5bd"),
		Number: 10,
	}

	firstSignatureArray := mustHexTo64BArray(t, "0xd7292caacc62504365f179892a7399f233944bf261f8a3f66260f70e0016f2d"+
		"b63922726b015c82dc7131f4730fbec61f71672a571453e51029bfb469070900f")

	secondSignatureArray := mustHexTo64BArray(t, "0xb3c408b74905dfedfffa66f99f16fe8b938fd8df76a92225228a1ca07523"+
		"0b99a2d9e173c561952e1e378b701915ca188d2c832ef92a3fab8e455f32570c0807")

	grandpaEquivocation := types.GrandpaEquivocation{
		RoundNumber:     1,
		ID:              identityPubKey.AsBytes(),
		FirstVote:       firstVote,
		FirstSignature:  firstSignatureArray,
		SecondVote:      secondVote,
		SecondSignature: secondSignatureArray,
	}
	preVoteEquivocation := types.PreVote(grandpaEquivocation)
	equivocationVote := types.NewGrandpaEquivocation()
	err = equivocationVote.SetValue(preVoteEquivocation)
	require.NoError(t, err)

	equivocationProof := types.GrandpaEquivocationProof{
		SetID:        1,
		Equivocation: *equivocationVote,
	}
	err = runtime.GrandpaSubmitReportEquivocationUnsignedExtrinsic(equivocationProof, opaqueKeyOwnershipProof)
	require.NoError(t, err)
}

func newTrieFromKeyValueListV1(t *testing.T, filename string) trie.Trie {
	data, err := os.ReadFile(filename)
	require.NoError(t, err)

	stateTrieResponse := make([]string, 0)
	err = json.Unmarshal(data, &stateTrieResponse)
	require.NoError(t, err)

	entries := make(map[string]string)
	for _, encEntry := range stateTrieResponse {
		entry := new(trie.Entry)
		err := scale.Unmarshal(common.MustHexToBytes(encEntry), entry)
		require.NoError(t, err)

		entries[common.BytesToHex(entry.Key)] = common.BytesToHex(entry.Value)
	}

	tr, err := inmemory_trie.LoadFromMap(entries, trie.V1)
	require.NoError(t, err)
	return tr
}

/*
TODO: fix downloading the state and block in the CI

func TestInstance_ExecuteBlock_PaseoRuntime_PaseoBlock3796856(t *testing.T) {
	paseoTrie := newTrieFromKeyValueListV1(t, "../test_data/paseo/block3796855.out")
	expectedRoot := common.MustHexToHash("0x420b69ab8c34629fc347acfdd4ac27ed8c5f7830b18387e8db03585265d02063")
	require.Equal(t, expectedRoot, trie.V1.MustHash(paseoTrie))

	state := storage.NewTrieState(paseoTrie)
	db, err := database.NewPebble("", true)
	require.NoError(t, err)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Info,
		NodeStorage: runtime.NodeStorage{
			LocalStorage:      db,
			PersistentStorage: db,
			BaseDB:            db,
		},
	}

	instance, err := NewInstanceFromTrie(paseoTrie, cfg)
	require.NoError(t, err)

	blockResponse := &messages.BlockResponseMessage{}

	data, err := os.ReadFile("../test_data/paseo/block3796856.out")
	require.NoError(t, err)

	err = blockResponse.Decode(common.MustHexToBytes(string(data))) //nolint:lll
	require.NoError(t, err)

	block := &types.Block{
		Header: *blockResponse.BlockData[0].Header,
		Body:   *blockResponse.BlockData[0].Body,
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)

	expectedRootNew := common.MustHexToHash("0xc6834534ce95a984926143ce7b5fa10c9fcf00727a6378690f8b2796544f2c85")
	require.Equal(t, expectedRootNew, state.Trie().MustHash())
}*/

func TestInstance_ExecuteBlock_PaseoRuntime_PaseoBlock1789153(t *testing.T) {
	paseoTrie := newTrieFromKeyValueListV1(t, "../test_data/paseo/block1789152.out")
	expectedRoot := common.MustHexToHash("0xf74a4a94758ac505a0bacdd52d7739d62b616eb03840d24d4a2df42df295c31d")
	require.Equal(t, expectedRoot, trie.V1.MustHash(paseoTrie))

	state := storage.NewInMemoryTrieState(paseoTrie)
	db, err := database.NewPebble("", true)
	require.NoError(t, err)

	cfg := Config{
		Storage: state,
		LogLvl:  log.Critical,
		NodeStorage: runtime.NodeStorage{
			LocalStorage:      db,
			PersistentStorage: db,
			BaseDB:            db,
		},
	}

	instance, err := NewInstanceFromTrie(paseoTrie, cfg)
	require.NoError(t, err)

	blockResponse := &messages.BlockResponseMessage{}
	err = blockResponse.Decode(common.MustHexToBytes("0x0a980f0a2000ab8c6e55a9951698052d9d999eb0c440c525af05867df39cb55863632b4ad912c702dacf47ace27c5ea78dc8dd9b3e22b02fef70865663511c8dc625c682adb92e7b86336d00c29a9d4465400c980cca388963461755040f2ba4c5ed722afc204014426e9080804403dad405e595bdca5f068ae91a75d455c90f3e7672a2ee62d3b506a93c210c0642414245b501012d00000080411411000000004cdbdb6fad62ab658029ea5891097221601991b27937b7d5d5631977e6eed1403aca4e0969fca166cd9442090a19800960743275ccde702ceb2380ca03a1410c4cbcb30f76e438b4f373f4d11f0e962717d993f8d5875f704bb62b1833b86b0a04424545468403e27785a60c1caf697109d8e173e3885432223ae08cc3a7736cb47578672228800542414245010174ee1ef2749cf4c906e7451df6f4c437e1c28cf43cde73e1ea0b20fa2e6b253559991104b5cb0ee7e89878bbf266c41a79609c9bb342d0ecd66b5cabd8abbb821a0b280403000b0028bf4a90011a9c0c69180436001c404082040000003c1c2072fc87375e6fb060fa2c4d2e2bb5e49655a4085ce81e9f38b4f8fc0c27b38aed1a2b14ce90ae52f299659b7e05d8e7a36f502b8b4e6b0b51bdee39b8864040820a00000046b4b8d09b32f8733851d3c52e1755baf47cdf94178d6d41f1046e7b27d24d20e5bd457dc11779174693cdca461ef5a554ab5aebc7b240f7e7dabb63db81eb8c4040821200000034780d63fe628c9ed70cfe4bf9bccab95885f7ad7dc8c9ef5c8bbf4c0ed4c7737e66975f5e36863a37847449a9bdd265c33473dd01c66d2eb6df56e2b54aee8c404082200000002c5e506d505990ef37036887292fea85e545f45c477512699fb209ce07f0085536b57a0b9eba6dc265134a2fc9548b72c2ed762c0d0e749cc40f4bb6fbf83a8d40408221000000f03f3ee19ecc74666b6733a57799ab73b50c1ede5df201e11849284b20b21a269bc18454d5b715e5a6bfbd454b37d39626db1bcb33bac1c78ab793680d42c3824040823200000024038d3803c9bfce4221c59913037affe5f7f67e2c85da9ae22a7de1a2b84315cc842618492e1157fa344b350840d3b451f1a17b863037c4f7f963c3c136158d40408233000000c2a2489e88287315fb5660ac8d614c90acb161acaa027f02640bfd647a4bcb01a33ca7efab99da38e12121f10294edf2f203a6d3c23b40b9bfb64215c2b0bb8204df070000dacf47ace27c5ea78dc8dd9b3e22b02fef70865663511c8dc625c682adb92e7b1421bbd8065689fbffd0015ec824862c247c74efb81d7097c8916ce538d1000a6da866424c6851d32a2dec2a17af4895b439f48bcbd15e8bd29cb90dc36881b1cadb7a887993a9d0389947e10fe20354e3e4bcd288589d5227b4882414f11c73414a850a6eea37caa076d56c409108f259982204b35471ccdc77d15343afd8ae661d3dd5391a9939cd36c2b50a174a4639ae726e27a39555892e401b84341754b6d8e34c5047efeb00080de9223931f3806f09642aae5765317d76103907898e3dfd7cc6839082ac61c25874fd304aa9f04c9c6ab22669007b9779d018f754fded4acef6aaa37f3d19eb3318fa5a9b62188ea2e488b2cb9a0af895ef496703c80000009103530676dcbb08f5a82b835bd38ab0fe7da0644dee97dd315291ad75b55a340261e652220076cf83e15e80a19af5bcaea0785d4e48d229cdf5dd153864350478f15087f57d04f99b61a2da580b7a37ca571fc01082ce68442eeb908b379f07ae44b9046fcc0c066175726120bf208a0800000000045250535290f74a4a94758ac505a0bacdd52d7739d62b616eb03840d24d4a2df42df295c31d82336d0005617572610101da85d66ac9895ad47f17997a74dc59b702b508eabdb102df271937688041d842f1c824c1cbc7e5ae703d761043b472c383e4712f7cac6b401dadf7530c5a3c8000000000e04c1b000c01fe7447034f72f3147c7f8f597acc64e9a451976dccc44222e9bf4490980d7b546f4341e563634c0e121bf2520381c35ed28746afc8904904fce2fba1551a4d8001364d7f7c48ae3e22d558a0b940ae51225dacc714950d322ed03733adb6c4be442a946caff686b716b605acf529ff7353f279efb104960a8fbf8433851b8ec88902844ccafbb3dae450f7c9bb870167d69b5d76a44ac975539da3b8d9ff908a6e2489d61daf3f2db5e188a235db74ea5d65eea9c9fbfd06e6c068f2abef52781e8610070089dbf7d702484000b3b22f06e497e4b1babeebdebffd34104e7becda11f77ada82336d00f74a4a94758ac505a0bacdd52d7739d62b616eb03840d24d4a2df42df295c31d6a489ada4a1a7e60a4418b0e7e9c037f21096d55e71dd479fa06c2fcc2893ed3100642414245b50103140000007f41141100000000a61518d94943fdb3099b7152ae8853406b43181d4323902010619ce327c8241a7bac1d6623d1bcf08c1579db3ea6a192ddafd0177c936808eccdec4892f47007d2b4c93b9ee38019acf47b272474ab31ee2f9e905fe6d52a43cb4de1018a4a00044245454684039b9b32499e519a43c1242be468073cce8a7b70604bd1766706bea09cd66889d70805424142450101b83da521999ad8d04448cf4aea523f4ecd32e9ad568daf47d9e02811408b8817f85e7865018512510f6da3fe0cce92a173622b3411fcff81802e3d7316cbd58e")) //nolint:lll
	require.NoError(t, err)

	block := &types.Block{
		Header: *blockResponse.BlockData[0].Header,
		Body:   *blockResponse.BlockData[0].Body,
	}

	_, err = instance.ExecuteBlock(block)
	require.NoError(t, err)

	expectedRootNew := common.MustHexToHash("0xc29a9d4465400c980cca388963461755040f2ba4c5ed722afc204014426e9080")

	root, err := state.Root()
	require.NoError(t, err)
	require.Equal(t, expectedRootNew, root)
}
