github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/config/protocol_config_test.go (about)

     1  package config
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"path/filepath"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/nspcc-dev/neo-go/internal/testserdes"
    11  	"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
    12  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    13  	"github.com/stretchr/testify/require"
    14  	"gopkg.in/yaml.v3"
    15  )
    16  
    17  func TestProtocolConfigurationValidation(t *testing.T) {
    18  	p := &ProtocolConfiguration{
    19  		StandbyCommittee: []string{
    20  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    21  		},
    22  		ValidatorsCount: 1,
    23  		TimePerBlock:    time.Microsecond,
    24  	}
    25  	require.Error(t, p.Validate())
    26  	p = &ProtocolConfiguration{
    27  		ValidatorsCount: 1,
    28  	}
    29  	require.Error(t, p.Validate())
    30  	p = &ProtocolConfiguration{
    31  		StandbyCommittee: []string{
    32  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    33  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    34  		},
    35  		ValidatorsCount: 3,
    36  	}
    37  	require.Error(t, p.Validate())
    38  	p = &ProtocolConfiguration{
    39  		StandbyCommittee: []string{
    40  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    41  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    42  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    43  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    44  		},
    45  		ValidatorsCount:   4,
    46  		ValidatorsHistory: map[uint32]uint32{0: 4},
    47  	}
    48  	require.Error(t, p.Validate())
    49  	p = &ProtocolConfiguration{
    50  		StandbyCommittee: []string{
    51  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    52  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    53  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    54  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    55  		},
    56  		CommitteeHistory:  map[uint32]uint32{0: 4},
    57  		ValidatorsHistory: map[uint32]uint32{0: 4, 1000: 5},
    58  	}
    59  	require.Error(t, p.Validate())
    60  	p = &ProtocolConfiguration{
    61  		StandbyCommittee: []string{
    62  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    63  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    64  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    65  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    66  		},
    67  		CommitteeHistory:  map[uint32]uint32{0: 4, 1000: 5},
    68  		ValidatorsHistory: map[uint32]uint32{0: 4},
    69  	}
    70  	require.Error(t, p.Validate())
    71  	p = &ProtocolConfiguration{
    72  		StandbyCommittee: []string{
    73  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    74  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    75  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    76  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    77  		},
    78  		CommitteeHistory:  map[uint32]uint32{0: 1, 999: 4},
    79  		ValidatorsHistory: map[uint32]uint32{0: 1},
    80  	}
    81  	require.Error(t, p.Validate())
    82  	p = &ProtocolConfiguration{
    83  		StandbyCommittee: []string{
    84  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    85  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    86  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    87  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    88  		},
    89  		CommitteeHistory:  map[uint32]uint32{0: 1, 1000: 4},
    90  		ValidatorsHistory: map[uint32]uint32{0: 1, 999: 4},
    91  	}
    92  	require.Error(t, p.Validate())
    93  	p = &ProtocolConfiguration{
    94  		StandbyCommittee: []string{
    95  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
    96  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
    97  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
    98  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
    99  		},
   100  		CommitteeHistory:  map[uint32]uint32{0: 1, 100: 4},
   101  		ValidatorsHistory: map[uint32]uint32{0: 4, 100: 4},
   102  	}
   103  	require.Error(t, p.Validate())
   104  	p = &ProtocolConfiguration{
   105  		StandbyCommittee: []string{
   106  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   107  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
   108  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
   109  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
   110  		},
   111  		CommitteeHistory:  map[uint32]uint32{0: 0, 100: 4},
   112  		ValidatorsHistory: map[uint32]uint32{0: 1, 100: 4},
   113  	}
   114  	require.Error(t, p.Validate())
   115  	p = &ProtocolConfiguration{
   116  		StandbyCommittee: []string{
   117  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   118  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
   119  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
   120  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
   121  		},
   122  		CommitteeHistory:  map[uint32]uint32{0: 1, 100: 4},
   123  		ValidatorsHistory: map[uint32]uint32{0: 0, 100: 4},
   124  	}
   125  	require.Error(t, p.Validate())
   126  	p = &ProtocolConfiguration{
   127  		StandbyCommittee: []string{
   128  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   129  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
   130  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
   131  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
   132  		},
   133  		CommitteeHistory:  map[uint32]uint32{0: 1, 100: 4},
   134  		ValidatorsHistory: map[uint32]uint32{0: 1, 100: 4},
   135  	}
   136  	require.NoError(t, p.Validate())
   137  	p = &ProtocolConfiguration{
   138  		StandbyCommittee:  []string{},
   139  		CommitteeHistory:  map[uint32]uint32{0: 1, 100: 4},
   140  		ValidatorsHistory: map[uint32]uint32{0: 1, 100: 4},
   141  	}
   142  	err := p.Validate()
   143  	require.Error(t, err)
   144  	require.Contains(t, err.Error(), "configuration should include StandbyCommittee")
   145  	p = &ProtocolConfiguration{
   146  		StandbyCommittee: []string{"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2"},
   147  	}
   148  	err = p.Validate()
   149  	require.Error(t, err)
   150  	require.Contains(t, err.Error(), "configuration should either have one of ValidatorsCount or ValidatorsHistory, not both")
   151  }
   152  
   153  func TestProtocolConfigurationValidation_Hardforks(t *testing.T) {
   154  	p := &ProtocolConfiguration{
   155  		Hardforks: map[string]uint32{
   156  			"Unknown": 123, // Unknown hard-fork.
   157  		},
   158  	}
   159  	require.Error(t, p.Validate())
   160  	p = &ProtocolConfiguration{
   161  		Hardforks: map[string]uint32{
   162  			"Aspidochelone": 2,
   163  			"Basilisk":      1, // Lower height in higher hard-fork.
   164  		},
   165  		StandbyCommittee: []string{
   166  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   167  		},
   168  		ValidatorsCount: 1,
   169  	}
   170  	require.Error(t, p.Validate())
   171  	p = &ProtocolConfiguration{
   172  		Hardforks: map[string]uint32{
   173  			"Aspidochelone": 2,
   174  			"Basilisk":      2, // Same height is OK.
   175  		}, StandbyCommittee: []string{
   176  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   177  		},
   178  		ValidatorsCount: 1,
   179  	}
   180  	require.NoError(t, p.Validate())
   181  	p = &ProtocolConfiguration{
   182  		Hardforks: map[string]uint32{
   183  			"Aspidochelone": 2,
   184  			"Basilisk":      3, // Larger height is OK.
   185  		},
   186  		StandbyCommittee: []string{
   187  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   188  		},
   189  		ValidatorsCount: 1,
   190  	}
   191  	require.NoError(t, p.Validate())
   192  	p = &ProtocolConfiguration{
   193  		Hardforks: map[string]uint32{
   194  			"Aspidochelone": 2,
   195  		},
   196  		StandbyCommittee: []string{
   197  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   198  		},
   199  		ValidatorsCount: 1,
   200  	}
   201  	require.NoError(t, p.Validate())
   202  	p = &ProtocolConfiguration{
   203  		Hardforks: map[string]uint32{
   204  			"Basilisk": 2,
   205  		},
   206  		StandbyCommittee: []string{
   207  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   208  		},
   209  		ValidatorsCount: 1,
   210  	}
   211  	require.NoError(t, p.Validate())
   212  }
   213  
   214  func TestGetCommitteeAndCNs(t *testing.T) {
   215  	p := &ProtocolConfiguration{
   216  		StandbyCommittee: []string{
   217  			"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2",
   218  			"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e",
   219  			"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699",
   220  			"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62",
   221  		},
   222  		CommitteeHistory:  map[uint32]uint32{0: 1, 100: 4},
   223  		ValidatorsHistory: map[uint32]uint32{0: 1, 200: 4},
   224  	}
   225  	require.Equal(t, 1, p.GetCommitteeSize(0))
   226  	require.Equal(t, 1, p.GetCommitteeSize(99))
   227  	require.Equal(t, 4, p.GetCommitteeSize(100))
   228  	require.Equal(t, 4, p.GetCommitteeSize(101))
   229  	require.Equal(t, 4, p.GetCommitteeSize(200))
   230  	require.Equal(t, 4, p.GetCommitteeSize(201))
   231  	require.Equal(t, 1, p.GetNumOfCNs(0))
   232  	require.Equal(t, 1, p.GetNumOfCNs(100))
   233  	require.Equal(t, 1, p.GetNumOfCNs(101))
   234  	require.Equal(t, 1, p.GetNumOfCNs(199))
   235  	require.Equal(t, 4, p.GetNumOfCNs(200))
   236  	require.Equal(t, 4, p.GetNumOfCNs(201))
   237  }
   238  
   239  func TestProtocolConfigurationEquals(t *testing.T) {
   240  	p := &ProtocolConfiguration{}
   241  	o := &ProtocolConfiguration{}
   242  	require.True(t, p.Equals(o))
   243  	require.True(t, o.Equals(p))
   244  	require.True(t, p.Equals(p))
   245  
   246  	cfg1, err := LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml"))
   247  	require.NoError(t, err)
   248  	cfg2, err := LoadFile(filepath.Join("..", "..", "config", "protocol.testnet.yml"))
   249  	require.NoError(t, err)
   250  	require.False(t, cfg1.ProtocolConfiguration.Equals(&cfg2.ProtocolConfiguration))
   251  
   252  	cfg2, err = LoadFile(filepath.Join("..", "..", "config", "protocol.mainnet.yml"))
   253  	require.NoError(t, err)
   254  	p = &cfg1.ProtocolConfiguration
   255  	o = &cfg2.ProtocolConfiguration
   256  	require.True(t, p.Equals(o))
   257  
   258  	o.CommitteeHistory = map[uint32]uint32{111: 7}
   259  	p.CommitteeHistory = map[uint32]uint32{111: 7}
   260  	require.True(t, p.Equals(o))
   261  	p.CommitteeHistory[111] = 8
   262  	require.False(t, p.Equals(o))
   263  
   264  	o.CommitteeHistory = nil
   265  	p.CommitteeHistory = nil
   266  
   267  	p.Hardforks = map[string]uint32{"Fork": 42}
   268  	o.Hardforks = map[string]uint32{"Fork": 42}
   269  	require.True(t, p.Equals(o))
   270  	p.Hardforks = map[string]uint32{"Fork2": 42}
   271  	require.False(t, p.Equals(o))
   272  
   273  	p.Hardforks = nil
   274  	o.Hardforks = nil
   275  
   276  	p.SeedList = []string{"url1", "url2"}
   277  	o.SeedList = []string{"url1", "url2"}
   278  	require.True(t, p.Equals(o))
   279  	p.SeedList = []string{"url11", "url22"}
   280  	require.False(t, p.Equals(o))
   281  
   282  	p.SeedList = nil
   283  	o.SeedList = nil
   284  
   285  	p.StandbyCommittee = []string{"key1", "key2"}
   286  	o.StandbyCommittee = []string{"key1", "key2"}
   287  	require.True(t, p.Equals(o))
   288  	p.StandbyCommittee = []string{"key2", "key1"}
   289  	require.False(t, p.Equals(o))
   290  
   291  	p.StandbyCommittee = nil
   292  	o.StandbyCommittee = nil
   293  
   294  	o.ValidatorsHistory = map[uint32]uint32{111: 0}
   295  	p.ValidatorsHistory = map[uint32]uint32{111: 0}
   296  	require.True(t, p.Equals(o))
   297  	p.ValidatorsHistory = map[uint32]uint32{112: 0}
   298  	require.False(t, p.Equals(o))
   299  }
   300  
   301  func TestGenesisExtensionsMarshalYAML(t *testing.T) {
   302  	pk, err := keys.NewPrivateKey()
   303  	require.NoError(t, err)
   304  	pub := pk.PublicKey()
   305  
   306  	t.Run("MarshalUnmarshalYAML", func(t *testing.T) {
   307  		g := &Genesis{
   308  			Roles: map[noderoles.Role]keys.PublicKeys{
   309  				noderoles.NeoFSAlphabet: {pub},
   310  				noderoles.P2PNotary:     {pub},
   311  			},
   312  			Transaction: &GenesisTransaction{
   313  				Script:    []byte{1, 2, 3, 4},
   314  				SystemFee: 123,
   315  			},
   316  		}
   317  		testserdes.MarshalUnmarshalYAML(t, g, new(Genesis))
   318  	})
   319  
   320  	t.Run("unmarshal config", func(t *testing.T) {
   321  		t.Run("good", func(t *testing.T) {
   322  			pubStr := pub.StringCompressed()
   323  			script := []byte{1, 2, 3, 4}
   324  			cfgYml := fmt.Sprintf(`ProtocolConfiguration:
   325    Genesis:
   326      Transaction:
   327        Script: "%s"
   328        SystemFee: 123
   329      Roles:
   330        NeoFSAlphabet:
   331          - %s
   332          - %s
   333        Oracle:
   334          - %s
   335          - %s`, base64.StdEncoding.EncodeToString(script), pubStr, pubStr, pubStr, pubStr)
   336  			cfg := new(Config)
   337  			require.NoError(t, yaml.Unmarshal([]byte(cfgYml), cfg))
   338  			require.Equal(t, 2, len(cfg.ProtocolConfiguration.Genesis.Roles))
   339  			require.Equal(t, keys.PublicKeys{pub, pub}, cfg.ProtocolConfiguration.Genesis.Roles[noderoles.NeoFSAlphabet])
   340  			require.Equal(t, keys.PublicKeys{pub, pub}, cfg.ProtocolConfiguration.Genesis.Roles[noderoles.Oracle])
   341  			require.Equal(t, &GenesisTransaction{
   342  				Script:    script,
   343  				SystemFee: 123,
   344  			}, cfg.ProtocolConfiguration.Genesis.Transaction)
   345  		})
   346  
   347  		t.Run("empty", func(t *testing.T) {
   348  			cfgYml := `ProtocolConfiguration:`
   349  			cfg := new(Config)
   350  			require.NoError(t, yaml.Unmarshal([]byte(cfgYml), cfg))
   351  			require.Nil(t, cfg.ProtocolConfiguration.Genesis.Transaction)
   352  			require.Empty(t, cfg.ProtocolConfiguration.Genesis.Roles)
   353  		})
   354  
   355  		t.Run("unknown role", func(t *testing.T) {
   356  			pubStr := pub.StringCompressed()
   357  			cfgYml := fmt.Sprintf(`ProtocolConfiguration:
   358    Genesis:
   359      Roles:
   360        BadRole:
   361          - %s`, pubStr)
   362  			cfg := new(Config)
   363  			err := yaml.Unmarshal([]byte(cfgYml), cfg)
   364  			require.Error(t, err)
   365  			require.Contains(t, err.Error(), "unknown node role: BadRole")
   366  		})
   367  
   368  		t.Run("last role", func(t *testing.T) {
   369  			pubStr := pub.StringCompressed()
   370  			cfgYml := fmt.Sprintf(`ProtocolConfiguration:
   371    Genesis:
   372      Roles:
   373        last:
   374          - %s`, pubStr)
   375  			cfg := new(Config)
   376  			err := yaml.Unmarshal([]byte(cfgYml), cfg)
   377  			require.Error(t, err)
   378  			require.Contains(t, err.Error(), "unknown node role: last")
   379  		})
   380  	})
   381  }